Remove usage of hardcoded native GOR token amount in backend

This commit is contained in:
Shreerang Kale 2025-07-23 18:45:55 +05:30
parent 375f33973e
commit 00dbc9ae8c
7 changed files with 49 additions and 52 deletions

View File

@ -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

View File

@ -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
);

View File

@ -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 {

View File

@ -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';

View File

@ -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<RequiredTokenInfo> {
// 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;

View File

@ -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<SolanaPaymentResult> {
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}`);

View File

@ -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();
}