Refactor paymentModal code

This commit is contained in:
Shreerang Kale 2025-07-24 18:41:33 +05:30
parent 161a333642
commit 32b583a637
7 changed files with 50 additions and 60 deletions

View File

@ -3,13 +3,15 @@
# Solana Payment Configuration
# TODO: Use different RPC URL or use browser wallet
NEXT_PUBLIC_SOLANA_RPC_URL=https://skilled-prettiest-seed.solana-mainnet.quiknode.pro/eeecfebd04e345f69f1900cc3483cbbfea02a158
NEXT_PUBLIC_GORBAGANA_RPC_URL=https://rpc.gorbagana.wtf
NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS=71Jvq4Epe2FCJ7JFSF7jLXdNk1Wy4Bhqd9iL6bEFELvg
NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL=GOR
NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD=5 # Payment amount in USD
NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD=5
# Gorbagana Chain Configuration
NEXT_PUBLIC_GORBAGANA_RPC_URL=https://rpc.gorbagana.wtf
NEXT_PUBLIC_ENABLE_NATIVE_GOR_TRANSFER=true
# Multisig address
# Multisig Address
NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS=FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY
# UI Configuration

View File

@ -11,11 +11,11 @@ import { verifyUnusedSolanaPayment } from '@/utils/solana-verify';
import { transferLNTTokens } from '@/services/laconic-transfer';
import { getRegistry, getRegistryConfig } from '@/config';
import { getRequiredTokenInfo } from '@/services/jupiter-price';
import { WRAPPED_SOL_MINT_ADDRESS } from '@/constants/payments';
import { IS_NAT_GOR_TRANSFER_ENABLED, WRAPPED_SOL_MINT_ADDRESS } from '@/constants/payments';
import { PaymentMethod } from '@/types';
assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required');
assert(process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL, 'GORBAGANA_RPC_URL is required');
assert(!IS_NAT_GOR_TRANSFER_ENABLED || process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL, 'GORBAGANA_RPC_URL is required when NAT GOR transfer is enabled');
const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL;
const GORBAGANA_RPC_URL = process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL;

View File

@ -10,7 +10,7 @@ import { BackpackWalletName } from '@solana/wallet-adapter-backpack';
import URLForm from '@/components/URLForm';
import StatusDisplay from '@/components/StatusDisplay';
import { createApplicationDeploymentRequest } from '@/services/registry';
import { PAYMENT_METHOD_LABELS } from '@/constants/payments';
import { IS_NAT_GOR_TRANSFER_ENABLED, PAYMENT_METHOD_LABELS } from '@/constants/payments';
import { usePaymentMethod } from '@/contexts/PaymentMethodContext';
import { PaymentMethod } from '@/types';
@ -36,16 +36,14 @@ export default function Home() {
const [error, setError] = useState<string | null>(null);
const [incorrectChainWarining, setIncorrectChainWarining] = useState<string | null>(null);
const isNatGorEnabled = process.env.NEXT_PUBLIC_ENABLE_NATIVE_GOR_TRANSFER === "true";
useEffect(() => {
if (!isNatGorEnabled) {
if (!IS_NAT_GOR_TRANSFER_ENABLED) {
setSelectedPaymentMethod(PaymentMethod.SPL_TOKEN);
}
}, [isNatGorEnabled, setSelectedPaymentMethod]);
}, [setSelectedPaymentMethod]);
useEffect(() => {
if (!wallet || wallet.adapter.name !== BackpackWalletName) {
if (!wallet || wallet.adapter.name !== BackpackWalletName || selectedPaymentMethod !== PaymentMethod.NAT_GOR) {
return;
}
@ -58,7 +56,7 @@ export default function Home() {
}
warnOnIncorrectChain();
}, [wallet]);
}, [wallet, selectedPaymentMethod]);
// Track previous payment method to detect switches
const previousPaymentMethodRef = useRef<PaymentMethod | null>(null);
@ -151,7 +149,7 @@ export default function Home() {
</h1>
{/* Step 1: Payment Method Selection */}
{ isNatGorEnabled &&
{ IS_NAT_GOR_TRANSFER_ENABLED &&
<div className="mb-10 p-6 rounded-lg" style={{ background: 'var(--muted-light)', borderLeft: '4px solid var(--primary)' }}>
<h2 className="text-lg font-semibold mb-4 flex items-center">
Choose Payment Method

View File

@ -1,18 +1,18 @@
'use client';
import { useCallback, useMemo, useState, useEffect } from 'react';
import { useCallback, useState, useEffect } from 'react';
import assert from 'assert';
import { Connection, LAMPORTS_PER_SOL } from '@solana/web3.js';
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { sendSolanaPayment, getRecipientAddress } from '@/services/solana';
import { sendSolanaPayment } from '@/services/solana';
import { getRequiredTokenInfo, RequiredTokenInfo } from '@/services/jupiter-price';
import { PaymentMethod, PaymentModalProps, PaymentRequest } from '@/types';
import { PAYMENT_METHOD_LABELS, WRAPPED_SOL_MINT_ADDRESS } from '@/constants/payments';
import { IS_NAT_GOR_TRANSFER_ENABLED, PAYMENT_METHOD_LABELS, WRAPPED_SOL_MINT_ADDRESS } from '@/constants/payments';
import { usePaymentMethod } from '@/contexts/PaymentMethodContext';
assert(process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL, 'GORBAGANA_RPC_URL is required');
assert(!IS_NAT_GOR_TRANSFER_ENABLED || process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL, 'GORBAGANA_RPC_URL is required when NAT GOR transfer is enabled');
const GORBAGANA_RPC_URL = process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL;
@ -24,7 +24,7 @@ export default function PaymentModal({
}: PaymentModalProps) {
const { selectedPaymentMethod: paymentMethod } = usePaymentMethod();
const { connection } = useConnection();
const { connection: solanaConnection } = useConnection();
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
@ -34,17 +34,10 @@ export default function PaymentModal({
const { wallet, publicKey } = useWallet();
const solanaConnection = connection;
const gorbaganaConnection = useMemo(() =>
GORBAGANA_RPC_URL ? new Connection(GORBAGANA_RPC_URL) : solanaConnection,
[solanaConnection]
);
// Get configuration from environment variables
const targetUsdAmount = parseFloat(process.env.NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD!);
const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!;
const tokenSymbol = process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL || 'TOKEN';
const tokenSymbol = process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL;
// Fetch payment amount based on USD price for both payment methods
useEffect(() => {
@ -113,11 +106,10 @@ export default function PaymentModal({
const paymentRequest: PaymentRequest = {
paymentMethod: paymentMethod,
amount: tokenAmount,
recipientAddress: getRecipientAddress(paymentMethod)
};
// Use different RPC connection based on payment method
const connectionToUse = paymentMethod === PaymentMethod.NAT_GOR ? gorbaganaConnection : solanaConnection;
const connectionToUse = paymentMethod === PaymentMethod.NAT_GOR ? new Connection(GORBAGANA_RPC_URL!) : solanaConnection;
const result = await sendSolanaPayment(
wallet.adapter,
@ -136,7 +128,7 @@ export default function PaymentModal({
} finally {
setLoading(false);
}
}, [paymentMethod, tokenAmount, loadingPrice, wallet, solanaConnection, gorbaganaConnection, publicKey, onPaymentComplete]);
}, [paymentMethod, tokenAmount, loadingPrice, wallet, solanaConnection, publicKey, onPaymentComplete]);
const getPaymentAmountDisplay = () => {
if (loadingPrice) return 'Loading...';
@ -187,7 +179,7 @@ export default function PaymentModal({
<div>
<p className="text-sm mb-2 font-medium" style={{ color: 'var(--muted)' }}>Recipient Address:</p>
<div className="p-3 rounded-md overflow-hidden" style={{ background: 'var(--muted-light)', color: 'var(--foreground)' }}>
<code className="text-sm font-mono break-all block">{getRecipientAddress(paymentMethod)}</code>
<code className="text-sm font-mono break-all block">{process.env.NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS}</code>
</div>
</div>

View File

@ -3,10 +3,9 @@ import { PaymentMethod } from "@/types";
// Payment method labels for UI
export const PAYMENT_METHOD_LABELS: Record<PaymentMethod, string> = {
[PaymentMethod.NAT_GOR]: 'GOR (native)',
[PaymentMethod.SPL_TOKEN]: process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL || 'SPL Token'
[PaymentMethod.SPL_TOKEN]: process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL!
};
// Default payment method (none selected initially)
export const DEFAULT_PAYMENT_METHOD: PaymentMethod | null = null;
export const WRAPPED_SOL_MINT_ADDRESS = 'So11111111111111111111111111111111111111112';
export const IS_NAT_GOR_TRANSFER_ENABLED = process.env.NEXT_PUBLIC_ENABLE_NATIVE_GOR_TRANSFER === "true";

View File

@ -46,7 +46,6 @@ export interface PaymentModalProps {
export interface PaymentRequest {
paymentMethod: PaymentMethod;
amount: number; // in base units (lamports for native GOR, token base units for SPL)
recipientAddress: string;
}
export interface LaconicTransferResult {

View File

@ -35,31 +35,31 @@ const extractTxInfo = async (
return { authority: source, amount: lamports.toString(), destination };
case PaymentMethod.SPL_TOKEN:
// Look for token transfer instruction using TOKEN_PROGRAM_ID
transferInstruction = result.transaction.message.instructions.find(
(instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID)
);
// Look for token transfer instruction using TOKEN_PROGRAM_ID
transferInstruction = result.transaction.message.instructions.find(
(instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID)
);
if (!transferInstruction || !('parsed' in transferInstruction)) {
throw new Error('SPL token transfer instruction not found');
}
if (!transferInstruction || !('parsed' in transferInstruction)) {
throw new Error('SPL token transfer instruction not found');
}
const parsed = transferInstruction.parsed;
const parsed = transferInstruction.parsed;
// Handle both transferChecked and transfer types
if (parsed.type === 'transferChecked') {
const { info: { tokenAmount, authority, destination } } = parsed;
return {
authority,
amount: tokenAmount.amount,
destination
};
} else if (parsed.type === 'transfer') {
const { info: { amount, authority, destination } } = parsed;
return { authority, amount, destination };
}
// Handle both transferChecked and transfer types
if (parsed.type === 'transferChecked') {
const { info: { tokenAmount, authority, destination } } = parsed;
return {
authority,
amount: tokenAmount.amount,
destination
};
} else if (parsed.type === 'transfer') {
const { info: { amount, authority, destination } } = parsed;
return { authority, amount, destination };
}
throw new Error('Unsupported token transfer type');
throw new Error('Unsupported token transfer type');
default:
throw new Error('Invalid payment method');
@ -119,10 +119,10 @@ export const verifyUnusedSolanaPayment = async (
}
// Extract transaction info based on payment method
const transferInfo = await extractTxInfo(connection, transactionSignature, paymentMethod);
const amount = transferInfo.amount;
const authority = transferInfo.authority;
const destination = transferInfo.destination;
const transferInfo = await extractTxInfo(connection, transactionSignature, paymentMethod);
const amount = transferInfo.amount;
const authority = transferInfo.authority;
const destination = transferInfo.destination;
// Verify amount using BN comparison
const transactionAmount = new BN(amount);