💰
Megapot
  • Overview
    • About Megapot
    • How to play
    • How to provide liquidity
    • How to refer users
    • Earn as a liquidity provider
    • About the Team
    • Megapoints
  • Deep dive
    • Components
    • System Diagram
    • Smart Contract
    • Provably Fair
  • Developers
    • Start Here
    • React UI Kit (Beta)
      • MegapotProvider
      • Jackpot
      • useJackpot
    • Standalone Integration
      • Getting Started
      • Contract Functions
      • Jackpot Page
      • LP Deposit Page
      • History Page
    • Custom ERC-20 Jackpot
  • Developer Reference
    • Testnet & Mainnet
    • Contract Overview & Functions
    • Megapot API
    • Megapot Examples
      • Refer Tickets
      • Gifting Tickets
    • Brand Kit
  • Appendix
    • VIP Program
    • FAQ
    • About Megapot
  • Terms of Service
  • Privacy Policy
  • Responsible Gaming
Powered by GitBook
On this page
  • LP Pool Status
  • Liquidity Component
  • Lp Pool Status
  • Lp Deposit Form
  • Deposit Input
  • Min Lp Deposit
  • Risk Percentage
  • Form Button
  • Lp Balances
  • Adjust Risk Percentage
  1. Developers
  2. Standalone Integration

LP Deposit Page

PreviousJackpot PageNextHistory Page

Last updated 1 month ago

This page will display a form to add & edit liquidity to the jackpot. It also lets users adjust their risk percentage (Position in Range) for the current jackpot as well as withdraw their liquidity.

This references our Jackpot Demo Integration Github Repo. You can find the code for the demo integration .

LP Pool Status

This component will display the status of the LP Pool.

// /app/components/lp-pool-status.tsx

import { LiquidityComponent } from '@/components/lp-component';

export default function LiquidityPage() {
    return <LiquidityComponent />;
}

Liquidity Component

This component will display the liquidity component.

'use client';

import { getLpPoolStatus, getLpsInfo } from '@/lib/contract';
import { useEffect, useState, useCallback } from 'react';
import { useAccount } from 'wagmi';
import { LpDepositForm } from './lp-components/lp-deposit-form';
import { LpPoolStatus } from './lp-components/lp-pool-status';
import { UserLpBalances } from './lp-components/user-lp-balances';

interface LpState {
    lpsInfo?: [bigint, bigint, bigint, boolean];
    lpPoolStatus?: boolean;
    isLoading: boolean;
    error?: string;
}

export function LiquidityComponent() {
    const { address, isConnected } = useAccount();
    const [state, setState] = useState<LpState>({
        isLoading: true,
    });

    const fetchLpPoolStatus = useCallback(async () => {
        try {
            const status = await getLpPoolStatus();
            setState((prev) => ({ ...prev, lpPoolStatus: status }));
        } catch (error) {
            setState((prev) => ({
                ...prev,
                error: 'Failed to fetch pool status',
            }));
        } finally {
            setState((prev) => ({ ...prev, isLoading: false }));
        }
    }, []);

    const fetchLpsInfo = useCallback(async () => {
        if (!address) return;

        setState((prev) => ({ ...prev, isLoading: true }));
        try {
            const info = await getLpsInfo(address as `0x${string}`);
            setState((prev) => ({ ...prev, lpsInfo: info }));
        } catch (error) {
            setState((prev) => ({
                ...prev,
                error: 'Failed to fetch LP info',
                lpsInfo: undefined,
            }));
        } finally {
            setState((prev) => ({ ...prev, isLoading: false }));
        }
    }, [address]);

    useEffect(() => {
        fetchLpPoolStatus();
        return () => {
            setState({ isLoading: true });
        };
    }, [fetchLpPoolStatus]);

    useEffect(() => {
        if (isConnected && address) {
            fetchLpsInfo();
        }
    }, [isConnected, address, fetchLpsInfo]);

    if (state.error) {
        return <div className="text-red-500">{state.error}</div>;
    }

    return (
        <div className="space-y-6">
            {state.isLoading ? (
                <div className="animate-pulse">Loading...</div>
            ) : (
                <>
                    <LpPoolStatus poolStatus={state.lpPoolStatus ?? false} />
                    {state.lpPoolStatus && address && (
                        <LpDepositForm address={address} />
                    )}
                    {state.lpsInfo && state.lpsInfo[0] > BigInt(0) && (
                        <UserLpBalances lpInfo={state.lpsInfo} />
                    )}
                </>
            )}
        </div>
    );
}

Lp Pool Status

This component will display the status of the LP Pool. Whether it is open or closed.

// /app/components/lp-components/lp-pool-status.tsx

import { Card, CardContent } from '../ui/card';
export function LpPoolStatus({
    poolStatus,
}: {
    poolStatus: boolean | undefined;
}) {
    return (
        <Card className="bg-white rounded-xl shadow-sm">
            <CardContent className="p-6">
                <div className="text-center">
                    <h2 className="text-lg font-medium text-gray-500 mb-2">
                        LP Pool Status
                    </h2>
                    <p className="text-4xl font-bold text-emerald-500">
                        {poolStatus ? 'Open' : 'Closed'}
                    </p>
                </div>
            </CardContent>
        </Card>
    );
}

Lp Deposit Form

This component will display the form to add & edit liquidity to the jackpot as well as set the initial risk percentage (Position in Range).

// /app/components/lp-components/lp-deposit-form.tsx

import { Card, CardContent } from '@/components/ui/card';
import { CONTRACT_ADDRESS, ERC20_TOKEN_ADDRESS } from '@/lib/constants';
import {
    getLpsInfo,
    getMinLpDeposit,
    getTokenAllowance,
    getTokenBalance,
} from '@/lib/contract';
import { useEffect, useState } from 'react';
import { parseAbi } from 'viem';
import { useWriteContract } from 'wagmi';
import { DepositInput } from './lp-deposit-form/deposit-input';
import { FormButton } from './lp-deposit-form/form-button';
import { MinLpDeposit } from './lp-deposit-form/min-lp-deposit';
import { RiskPercentage } from './lp-deposit-form/risk-percentage';
export function LpDepositForm({ address }: { address: string }) {
    const [state, setState] = useState({
        walletAddress: undefined as `0x${string}` | undefined,
        walletBalance: 0,
        allowance: 0,
        lpPrincipal: 0,
        currentRiskPercentage: null as number | null,
        newRiskPercentage: 10,
        minLpDeposit: undefined as number | undefined,
        walletFunded: false,
        allowanceFunded: false,
        depositAmount: 250,
        tempDepositAmount: 250,
    });

    const { writeContract } = useWriteContract();

    useEffect(() => {
        setState((prev) => ({
            ...prev,
            walletAddress: address as `0x${string}`,
        }));

        const fetchMinLpDeposit = async () => {
            const minDeposit = await getMinLpDeposit();
            if (minDeposit) {
                setState((prev) => ({
                    ...prev,
                    minLpDeposit: Number(minDeposit),
                }));
            }
        };
        fetchMinLpDeposit();

        const fetchLpInfo = async () => {
            if (!state.walletAddress) return;
            const lpsInfo = await getLpsInfo(state.walletAddress);
            if (lpsInfo) {
                setState((prev) => ({
                    ...prev,
                    currentRiskPercentage: Number(lpsInfo[2]),
                    lpPrincipal: Number(lpsInfo[0]),
                }));
            }
        };
        fetchLpInfo();
    }, [address]);

    const fetchWalletAndAllowance = async () => {
        if (!state.walletAddress) return;

        try {
            const balance = await getTokenBalance(state.walletAddress);
            setState((prev) => ({
                ...prev,
                walletBalance: balance
                    ? Number((balance / 10 ** 6).toFixed(0))
                    : 0,
            }));
        } catch (error) {
            console.error('Error fetching wallet balance:', error);
        }

        try {
            const allowance = await getTokenAllowance(address as `0x${string}`);
            setState((prev) => ({
                ...prev,
                allowance: allowance ? allowance / 10 ** 6 : 0,
            }));
        } catch (error) {
            console.error('Error fetching allowance:', error);
        }
    };

    useEffect(() => {
        fetchWalletAndAllowance();
        const interval = setInterval(fetchWalletAndAllowance, 30000);
        return () => clearInterval(interval); // Cleanup interval on unmount
    }, [state.walletAddress]);

    const handleApprove = async () => {
        const depositAmount = state.minLpDeposit;
        try {
            const lpDepositAbi = [
                'function approve(address spender, uint256 amount) returns (bool)',
            ];

            writeContract?.({
                abi: parseAbi(lpDepositAbi),
                address: ERC20_TOKEN_ADDRESS as `0x${string}`,
                functionName: 'approve',
                args: [CONTRACT_ADDRESS as `0x${string}`, depositAmount],
            });
        } catch (error) {
            console.error('Error approving token:', error);
        }
    };

    const handleDeposit = async () => {
        const depositAmount = state.minLpDeposit;
        const riskPercentage = state.currentRiskPercentage;
        try {
            if (depositAmount === 0) {
                console.error('Deposit amount cannot be 0');
                return;
            }
            if (!riskPercentage || riskPercentage <= 0) {
                console.error('Risk percentage cannot be null');
                return;
            }
            const lpDepositAbi = [
                'function lpDeposit(uint256 amount, uint256 riskPercentage) returns (bool)',
            ];

            writeContract?.({
                abi: parseAbi(lpDepositAbi),
                address: CONTRACT_ADDRESS as `0x${string}`,
                functionName: 'lpDeposit',
                args: [depositAmount, riskPercentage],
            });
        } catch (error) {
            console.error('Error approving token:', error);
        }
    };

    return (
        <Card className="bg-white rounded-xl shadow-sm">
            <CardContent className="p-6">
                <div className="text-center">
                    <h2 className="text-lg font-medium text-gray-500 mb-2">
                        LP Deposit Form
                    </h2>
                    <form>
                        <DepositInput
                            walletBalance={state.walletBalance}
                            allowance={state.allowance}
                            setWalletFunded={(funded: boolean) =>
                                setState((prev) => ({
                                    ...prev,
                                    walletFunded: funded,
                                }))
                            }
                            setAllowanceFunded={(funded: boolean) =>
                                setState((prev) => ({
                                    ...prev,
                                    allowanceFunded: funded,
                                }))
                            }
                            setDepositAmount={(amount: number) =>
                                setState((prev) => ({
                                    ...prev,
                                    depositAmount: amount,
                                }))
                            }
                        />
                        {state.minLpDeposit && state.lpPrincipal === 0 && (
                            <MinLpDeposit minLpDeposit={state.minLpDeposit} />
                        )}
                        <RiskPercentage
                            newRiskPercentage={state.newRiskPercentage}
                            setNewRiskPercentage={(percentage: number) =>
                                setState((prev) => ({
                                    ...prev,
                                    newRiskPercentage: percentage,
                                }))
                            }
                        />
                    </form>
                </div>
                <div className="flex justify-center">
                    <FormButton
                        walletFunded={state.walletFunded}
                        allowanceFunded={state.allowanceFunded}
                        depositAmount={state.depositAmount}
                        tempDepositAmount={state.tempDepositAmount}
                        handleDeposit={handleDeposit}
                        handleApprove={handleApprove}
                        walletBalance={state.walletBalance}
                    />
                </div>
            </CardContent>
        </Card>
    );
}

Deposit Input

This component will display the input for the deposit amount & risk percentage (Position in Range).

// /app/components/lp-components/lp-deposit-form/deposit-input.tsx

import { useEffect, useState } from 'react';

export function DepositInput({
    walletBalance,
    allowance,
    setWalletFunded,
    setAllowanceFunded,
    setDepositAmount,
}: {
    walletBalance: number;
    allowance: number;
    setWalletFunded: (funded: boolean) => void;
    setAllowanceFunded: (funded: boolean) => void;
    setDepositAmount: (amount: number) => void;
}) {
    const [tempDepositAmount, setTempDepositAmount] = useState<number>(0);
    useEffect(() => {
        setDepositAmount(tempDepositAmount);
        // Ensure wallet and allowance funding states are updated based on tempDepositAmount
        setWalletFunded(
            walletBalance >= tempDepositAmount && tempDepositAmount > 0
        );
        setAllowanceFunded(
            allowance >= tempDepositAmount && tempDepositAmount > 0
        );
    }, [tempDepositAmount, walletBalance, allowance]);

    return (
        <>
            <label className="block text-left mb-1">Add USDC Amount</label>
            <input
                type="number"
                className="mt-1 block w-full h-10 rounded-md border-2 border-gray-500 shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-500 focus:ring-offset-0 sm:text-sm ps-2"
                min={0}
                value={tempDepositAmount}
                onChange={(e) => {
                    setTempDepositAmount(Number(e.target.value));
                    if (
                        walletBalance >= Number(e.target.value) &&
                        Number(e.target.value) > 0
                    ) {
                        setWalletFunded(true);
                    } else {
                        setWalletFunded(false);
                    }
                    if (
                        allowance >= Number(e.target.value) &&
                        Number(e.target.value) > 0
                    ) {
                        setAllowanceFunded(true);
                    } else {
                        setAllowanceFunded(false);
                    }
                }}
                onBlur={(e) => {
                    setDepositAmount(tempDepositAmount);
                }}
                style={{
                    appearance: 'none',
                    MozAppearance: 'textfield',
                }}
            />
        </>
    );
}

Min Lp Deposit

This component will display the minimum deposit amount for the current jackpot.

// /app/components/lp-components/lp-deposit-form/min-lp-deposit.tsx

export function MinLpDeposit({ minLpDeposit }: { minLpDeposit: number }) {
    return (
        <div>
            <p className="text-sm text-emerald-500">
                Minimum Initial LP Deposit: ${minLpDeposit / 10 ** 6} USDC
            </p>
        </div>
    );
}

Risk Percentage

This component will allow the user to adjust their risk percentage (Position in Range) for future jackpots.

// /app/components/lp-components/lp-deposit-form/risk-percentage.tsx

export function RiskPercentage({
    newRiskPercentage,
    setNewRiskPercentage,
}: {
    newRiskPercentage: number;
    setNewRiskPercentage: (percentage: number) => void;
}) {
    return (
        <>
            <label className="block text-left mb-1 mt-4">
                Set Risk Percentage
            </label>
            <select
                onChange={(e) => {
                    const value = e.target.value;
                    setNewRiskPercentage(
                        value === 'custom' ? 0 : Number(value)
                    );
                }}
                defaultValue={'10'}
                className="mt-1 block w-full h-10 rounded-md border-2 border-gray-500 shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-500 focus:ring-offset-0 sm:text-sm ps-2"
            >
                <option value="100">100%</option>
                <option value="50">50%</option>
                <option value="25">25%</option>
                <option value="10">10%</option>
                <option value="custom">Custom</option>
            </select>
            {newRiskPercentage === 0 && (
                <input
                    type="number"
                    min={0}
                    max={100}
                    onChange={(e) => {
                        const customValue = Number(e.target.value);
                        if (!isNaN(customValue) && customValue > 0) {
                            setNewRiskPercentage(customValue);
                        }
                    }}
                    className="mt-1 block w-full h-10 rounded-md border-2 border-gray-500 shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-500 focus:ring-offset-0 sm:text-sm ps-2"
                    placeholder="Enter custom risk percentage"
                    style={{
                        appearance: 'none',
                        MozAppearance: 'textfield',
                    }}
                />
            )}
        </>
    );
}

Form Button

This component will display the button to add & edit liquidity to the jackpot as well as set the initial risk percentage (Position in Range).

// /app/components/lp-components/lp-deposit-form/form-button.tsx

export function FormButton({
    walletFunded,
    allowanceFunded,
    depositAmount,
    tempDepositAmount,
    handleDeposit,
    handleApprove,
    walletBalance,
}: {
    walletFunded: boolean;
    allowanceFunded: boolean;
    depositAmount: number;
    tempDepositAmount: number;
    handleDeposit: () => void;
    handleApprove: () => void;
    walletBalance: number;
}) {
    let buttonContent;

    if (depositAmount === 0 || tempDepositAmount === 0) {
        buttonContent = (
            <button
                className="mt-4 bg-red-500 text-white px-4 py-2 rounded-md"
                disabled
            >
                Deposit Amount Cannot Be 0
            </button>
        );
    } else if (walletFunded && allowanceFunded && tempDepositAmount > 0) {
        buttonContent = (
            <button
                onClick={handleDeposit}
                className="mt-4 bg-blue-500 text-white px-4 py-2 rounded-md"
            >
                Deposit
            </button>
        );
    } else if (walletFunded && !allowanceFunded) {
        buttonContent = (
            <button
                onClick={handleApprove}
                className="mt-4 bg-blue-500 text-white px-4 py-2 rounded-md"
            >
                Need to Approve USDC
            </button>
        );
    } else if (!walletFunded && tempDepositAmount > walletBalance) {
        buttonContent = (
            <button
                className="mt-4 bg-red-500 text-white px-4 py-2 rounded-md"
                disabled
            >
                Need to Fund Wallet
            </button>
        );
    } else {
        buttonContent = (
            <button
                className="mt-4 bg-gray-500 text-white px-4 py-2 rounded-md"
                disabled
            >
                Enter Amount
            </button>
        );
    }

    return <>{buttonContent}</>;
}

Lp Balances

This component will display the user's LP balances.

  • Principal - This is how much liquidity is deposited but not in range. Position in Range is 100%

  • Position in Range - This is how much liquidity is in range. Position in Range is how much liquidity is at risk in the current jackpot.

  • Risk Percentage - This is the risk percentage (Position in Range) for the current jackpot.

// /app/components/lp-components/lp-balances.tsx

export function LpBalances({
    principal,
    inRange,
    currentRiskPercent,
}: {
    principal: number;
    inRange: number;
    currentRiskPercent: number;
}) {
    return (
        <>
            <div className="flex justify-between">
                <p className="font-bold text-emerald-500">Principal:</p>
                <p className="font-bold text-emerald-500">
                    {(principal / 10 ** 6).toFixed(2)}
                </p>
            </div>
            <div className="flex justify-between">
                <p className="font-bold text-emerald-500">Position In Range:</p>
                <p className="font-bold text-emerald-500">
                    {(inRange / 10 ** 6).toFixed(2)}
                </p>
            </div>
            <div className="flex justify-between">
                <p className="font-bold text-emerald-500">Risk Percent:</p>
                <p className="font-bold text-emerald-500">
                    {currentRiskPercent}%
                </p>
            </div>
        </>
    );
}

Adjust Risk Percentage

This component will allow the user to adjust their risk percentage (Position in Range) for future jackpots.

// /app/components/lp-components/adjust-risk-percentage.tsx

export function AdjustRiskPercentage({
    riskPercent,
    tempRiskPercent,
    setRiskPercent,
    setTempRiskPercent,
}: {
    riskPercent: number;
    tempRiskPercent: number;
    setRiskPercent: (riskPercent: number) => void;
    setTempRiskPercent: (riskPercent: number) => void;
}) {
    return (
        <>
            <label
                htmlFor="riskPercentInput"
                className="text-sm font-medium text-gray-700 w-1/2"
            >
                Adjust Risk %
            </label>
            <input
                id="riskPercentInput"
                type="number"
                min="0"
                max="100"
                value={tempRiskPercent}
                onChange={(e) => setTempRiskPercent(Number(e.target.value))}
                onBlur={() => {
                    setRiskPercent(tempRiskPercent);
                }}
                className="mt-1 block w-full h-10 rounded-md border-2 border-gray-500 shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-500 focus:ring-offset-0 sm:text-sm ps-2"
                style={{
                    appearance: 'none',
                    MozAppearance: 'textfield',
                }}
            />
        </>
    );
}
here