From 00dbc9ae8ce190e85b489e24c17630b82289943b Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Wed, 23 Jul 2025 18:45:55 +0530 Subject: [PATCH] Remove usage of hardcoded native GOR token amount in backend --- .env.example | 4 +-- src/app/api/registry/route.ts | 51 +++++++++++++++--------------- src/components/WalletProviders.tsx | 4 +-- src/constants/payments.ts | 3 -- src/services/jupiter-price.ts | 7 ++-- src/services/solana.ts | 14 ++++---- src/utils/solana-verify.ts | 18 +++++------ 7 files changed, 49 insertions(+), 52 deletions(-) diff --git a/.env.example b/.env.example index 5b86f19..dde6480 100644 --- a/.env.example +++ b/.env.example @@ -4,11 +4,11 @@ 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 # Multisig address NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS=FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY -NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL=GOR -NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD=5 # Payment amount in USD # UI Configuration NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app diff --git a/src/app/api/registry/route.ts b/src/app/api/registry/route.ts index 7cd0263..7b65443 100644 --- a/src/app/api/registry/route.ts +++ b/src/app/api/registry/route.ts @@ -10,8 +10,8 @@ import { DENOM as ALNT_DENOM } from '@cerc-io/registry-sdk'; import { verifyUnusedSolanaPayment } from '@/utils/solana-verify'; import { transferLNTTokens } from '@/services/laconic-transfer'; import { getRegistry, getRegistryConfig } from '@/config'; -import { getRequiredTokenInfo } from '@/services/jupiter-price'; -import { PaymentMethod, SOL_PAYMENT_AMOUNT_LAMPORTS } from '@/constants/payments'; +import { getRequiredNativeGorInfo, getRequiredTokenInfo } from '@/services/jupiter-price'; +import { PaymentMethod } from '@/constants/payments'; import { getRecipientAddress } from '@/services/solana'; assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required'); @@ -203,42 +203,43 @@ export async function POST(request: NextRequest) { // Verify Solana payment based on method console.log(`Step 0: Verifying Solana ${paymentMethod} payment...`); - let expectedAmount: BN; + + // Calculate expected token amount based on current price let expectedRecipientAddress: string; + let requiredAmountInBaseUnits: number; - if (paymentMethod === 'nat-gor') { - expectedAmount = new BN(SOL_PAYMENT_AMOUNT_LAMPORTS); - expectedRecipientAddress = getRecipientAddress('nat-gor'); - } else if (paymentMethod === 'spl-token') { - const targetUsdAmount = parseFloat(process.env.NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD!); - const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!; + const targetUsdAmount = parseFloat(process.env.NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD!); - // Calculate expected token amount based on current price - let expectedTokenAmount: number; - try { - const { requiredAmountInBaseUnits } = await getRequiredTokenInfo(targetUsdAmount, mintAddress); - expectedTokenAmount = Math.round(requiredAmountInBaseUnits - ALLOWED_SLIPPAGE_FACTOR * requiredAmountInBaseUnits); - } catch (error) { - console.error('Error calculating token amount:', error); + try { + if (paymentMethod === 'nat-gor') { + const requiredNativeGorInfo = await getRequiredNativeGorInfo(targetUsdAmount); + requiredAmountInBaseUnits = requiredNativeGorInfo.requiredAmountInBaseUnits; + expectedRecipientAddress = getRecipientAddress('nat-gor'); + } else if (paymentMethod === 'spl-token') { + const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!; + const requiredTokenInfo = await getRequiredTokenInfo(targetUsdAmount, mintAddress); + requiredAmountInBaseUnits = requiredTokenInfo.requiredAmountInBaseUnits; + expectedRecipientAddress = getRecipientAddress('spl-token'); + } else { return NextResponse.json({ status: 'error', - message: 'Unable to verify payment due to price calculation error' - }, { status: 500 }); + message: 'Unsupported payment method' + }, { status: 400 }); } - - expectedAmount = new BN(expectedTokenAmount); - expectedRecipientAddress = getRecipientAddress('spl-token'); - } else { + } catch (error) { + console.error('Error calculating expected amount:', error); return NextResponse.json({ status: 'error', - message: 'Unsupported payment method' - }, { status: 400 }); + message: 'Unable to verify payment due to price calculation error' + }, { status: 500 }); } + const expectedTokenAmount = Math.round(requiredAmountInBaseUnits - ALLOWED_SLIPPAGE_FACTOR * requiredAmountInBaseUnits); + const solanaPaymentResult = await verifyUnusedSolanaPayment( connection, txHash, - expectedAmount, + new BN(expectedTokenAmount), paymentMethod, expectedRecipientAddress ); diff --git a/src/components/WalletProviders.tsx b/src/components/WalletProviders.tsx index 4ef6b36..492e066 100644 --- a/src/components/WalletProviders.tsx +++ b/src/components/WalletProviders.tsx @@ -21,7 +21,7 @@ interface WalletProvidersProps { export default function WalletProviders({ children }: WalletProvidersProps) { const { selectedPaymentMethod } = usePaymentMethod(); - + // Configure the Solana network endpoint const endpoint = useMemo(() => { return SOLANA_RPC_URL; @@ -46,7 +46,7 @@ export default function WalletProviders({ children }: WalletProvidersProps) { return allWallets.filter(wallet => { const isBackpack = wallet.name.toLowerCase().includes('backpack'); - + if (selectedPaymentMethod === 'nat-gor') { return isBackpack; // Only Backpack for native GOR } else { diff --git a/src/constants/payments.ts b/src/constants/payments.ts index b8e462d..a12c0c2 100644 --- a/src/constants/payments.ts +++ b/src/constants/payments.ts @@ -1,8 +1,5 @@ // Payment configuration constants -// Native GOR payment amount in lamports (1 GOR = 1,000,000,000 lamports) -export const SOL_PAYMENT_AMOUNT_LAMPORTS = 10000000; // 0.01 GOR native - // Payment method types export type PaymentMethod = 'nat-gor' | 'spl-token'; diff --git a/src/services/jupiter-price.ts b/src/services/jupiter-price.ts index 0ba8dad..4fe7343 100644 --- a/src/services/jupiter-price.ts +++ b/src/services/jupiter-price.ts @@ -17,6 +17,8 @@ interface RequiredTokenInfo { decimals: number; } +const WRAPPED_SOL_MINT_ADDRESS = 'So11111111111111111111111111111111111111112'; + /** * Fetches token price from Jupiter aggregator API * @param mintAddress The Solana token mint address @@ -73,10 +75,7 @@ export async function getRequiredTokenInfo(targetUsdAmount: number, mintAddress: * @returns The GOR amount in lamports needed and decimals (always 9 for SOL/GOR) */ export async function getRequiredNativeGorInfo(targetUsdAmount: number): Promise { - // Wrapped SOL mint address - const wrappedSolMint = 'So11111111111111111111111111111111111111112'; - - const priceInfo = await getTokenInfo(wrappedSolMint); + const priceInfo = await getTokenInfo(WRAPPED_SOL_MINT_ADDRESS); // Calculate GOR amount needed (same as SOL) const gorAmount = targetUsdAmount / priceInfo.usdPrice; diff --git a/src/services/solana.ts b/src/services/solana.ts index a023187..88ebd67 100644 --- a/src/services/solana.ts +++ b/src/services/solana.ts @@ -11,7 +11,7 @@ import { import { WalletAdapter } from '@solana/wallet-adapter-base'; import { SolanaPaymentResult, PaymentRequest } from '../types'; -import { PaymentMethod, SOL_PAYMENT_AMOUNT_LAMPORTS } from '../constants/payments'; +import { PaymentMethod } from '../constants/payments'; assert(process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS, 'SOLANA_TOKEN_MINT_ADDRESS is required'); assert(process.env.NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS, 'SOLANA_TOKEN_RECIPIENT_ADDRESS is required'); @@ -38,7 +38,8 @@ async function findAssociatedTokenAddress( export async function sendSolPayment( wallet: WalletAdapter, connection: Connection, - walletPublicKey: string + walletPublicKey: string, + tokenAmount: BN ): Promise { try { if (!wallet.connected || !wallet.publicKey) { @@ -51,14 +52,14 @@ export async function sendSolPayment( console.log('Processing native GOR payment:', { sender: senderPublicKey.toBase58(), receiver: receiverPublicKey.toBase58(), - amount: SOL_PAYMENT_AMOUNT_LAMPORTS + amount: tokenAmount.toString() }); const transaction = new Transaction().add( SystemProgram.transfer({ fromPubkey: senderPublicKey, toPubkey: receiverPublicKey, - lamports: SOL_PAYMENT_AMOUNT_LAMPORTS, + lamports: BigInt(tokenAmount.toString()), }) ); @@ -226,11 +227,12 @@ export async function sendSolanaPayment( throw new Error('Wallet not connected'); } + const tokenAmount = new BN(paymentRequest.amount); + switch (paymentRequest.paymentMethod) { case 'nat-gor': - return await sendSolPayment(wallet, connection, walletPublicKey); + return await sendSolPayment(wallet, connection, walletPublicKey, tokenAmount); case 'spl-token': - const tokenAmount = new BN(paymentRequest.amount); return await sendSplTokenPayment(wallet, connection, walletPublicKey, tokenAmount); default: throw new Error(`Unsupported payment method: ${paymentRequest.paymentMethod}`); diff --git a/src/utils/solana-verify.ts b/src/utils/solana-verify.ts index b2cc0a8..f64ba83 100644 --- a/src/utils/solana-verify.ts +++ b/src/utils/solana-verify.ts @@ -5,7 +5,7 @@ import { PaymentMethod } from '../constants/payments'; // Extract transaction info for native GOR transfers const extractSolTransferInfo = async ( - connection: Connection, + connection: Connection, transactionSignature: string ): Promise<{ authority: string; amount: string; destination: string }> => { const result = await connection.getParsedTransaction(transactionSignature, 'confirmed'); @@ -29,7 +29,7 @@ const extractSolTransferInfo = async ( // Extract transaction info for SPL token transfers const extractSplTokenTransferInfo = async ( - connection: Connection, + connection: Connection, transactionSignature: string ): Promise<{ authority: string; amount: string; destination: string }> => { const result = await connection.getParsedTransaction(transactionSignature, 'confirmed'); @@ -48,14 +48,14 @@ const extractSplTokenTransferInfo = async ( } 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 + return { + authority, + amount: tokenAmount.amount, + destination }; } else if (parsed.type === 'transfer') { const { info: { amount, authority, destination } } = parsed; @@ -83,8 +83,6 @@ export const verifyUnusedSolanaPayment = async ( // Fetch transaction details const transactionResult = await connection.getParsedTransaction(transactionSignature, 'confirmed'); - console.dir(transactionResult, {depth: null}); - if (!transactionResult) { return { valid: false, @@ -167,7 +165,7 @@ export const verifyUnusedSolanaPayment = async ( recipientPublicKey, true // Allow off-curve addresses ); - + validRecipient = destination === expectedTokenAccount.toBase58(); }