Remove usage of hardcoded native GOR token amount in backend
This commit is contained in:
parent
375f33973e
commit
00dbc9ae8c
@ -4,11 +4,11 @@
|
|||||||
NEXT_PUBLIC_SOLANA_RPC_URL=https://skilled-prettiest-seed.solana-mainnet.quiknode.pro/eeecfebd04e345f69f1900cc3483cbbfea02a158
|
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_GORBAGANA_RPC_URL=https://rpc.gorbagana.wtf
|
||||||
NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS=71Jvq4Epe2FCJ7JFSF7jLXdNk1Wy4Bhqd9iL6bEFELvg
|
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
|
# Multisig address
|
||||||
NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS=FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY
|
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
|
# UI Configuration
|
||||||
NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app
|
NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app
|
||||||
|
@ -10,8 +10,8 @@ import { DENOM as ALNT_DENOM } from '@cerc-io/registry-sdk';
|
|||||||
import { verifyUnusedSolanaPayment } from '@/utils/solana-verify';
|
import { verifyUnusedSolanaPayment } from '@/utils/solana-verify';
|
||||||
import { transferLNTTokens } from '@/services/laconic-transfer';
|
import { transferLNTTokens } from '@/services/laconic-transfer';
|
||||||
import { getRegistry, getRegistryConfig } from '@/config';
|
import { getRegistry, getRegistryConfig } from '@/config';
|
||||||
import { getRequiredTokenInfo } from '@/services/jupiter-price';
|
import { getRequiredNativeGorInfo, getRequiredTokenInfo } from '@/services/jupiter-price';
|
||||||
import { PaymentMethod, SOL_PAYMENT_AMOUNT_LAMPORTS } from '@/constants/payments';
|
import { PaymentMethod } from '@/constants/payments';
|
||||||
import { getRecipientAddress } from '@/services/solana';
|
import { getRecipientAddress } from '@/services/solana';
|
||||||
|
|
||||||
assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required');
|
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
|
// Verify Solana payment based on method
|
||||||
console.log(`Step 0: Verifying Solana ${paymentMethod} payment...`);
|
console.log(`Step 0: Verifying Solana ${paymentMethod} payment...`);
|
||||||
|
|
||||||
let expectedAmount: BN;
|
|
||||||
|
// Calculate expected token amount based on current price
|
||||||
let expectedRecipientAddress: string;
|
let expectedRecipientAddress: string;
|
||||||
|
let requiredAmountInBaseUnits: number;
|
||||||
|
|
||||||
if (paymentMethod === 'nat-gor') {
|
const targetUsdAmount = parseFloat(process.env.NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD!);
|
||||||
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!;
|
|
||||||
|
|
||||||
// Calculate expected token amount based on current price
|
try {
|
||||||
let expectedTokenAmount: number;
|
if (paymentMethod === 'nat-gor') {
|
||||||
try {
|
const requiredNativeGorInfo = await getRequiredNativeGorInfo(targetUsdAmount);
|
||||||
const { requiredAmountInBaseUnits } = await getRequiredTokenInfo(targetUsdAmount, mintAddress);
|
requiredAmountInBaseUnits = requiredNativeGorInfo.requiredAmountInBaseUnits;
|
||||||
expectedTokenAmount = Math.round(requiredAmountInBaseUnits - ALLOWED_SLIPPAGE_FACTOR * requiredAmountInBaseUnits);
|
expectedRecipientAddress = getRecipientAddress('nat-gor');
|
||||||
} catch (error) {
|
} else if (paymentMethod === 'spl-token') {
|
||||||
console.error('Error calculating token amount:', error);
|
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({
|
return NextResponse.json({
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message: 'Unable to verify payment due to price calculation error'
|
message: 'Unsupported payment method'
|
||||||
}, { status: 500 });
|
}, { status: 400 });
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
expectedAmount = new BN(expectedTokenAmount);
|
console.error('Error calculating expected amount:', error);
|
||||||
expectedRecipientAddress = getRecipientAddress('spl-token');
|
|
||||||
} else {
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message: 'Unsupported payment method'
|
message: 'Unable to verify payment due to price calculation error'
|
||||||
}, { status: 400 });
|
}, { status: 500 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const expectedTokenAmount = Math.round(requiredAmountInBaseUnits - ALLOWED_SLIPPAGE_FACTOR * requiredAmountInBaseUnits);
|
||||||
|
|
||||||
const solanaPaymentResult = await verifyUnusedSolanaPayment(
|
const solanaPaymentResult = await verifyUnusedSolanaPayment(
|
||||||
connection,
|
connection,
|
||||||
txHash,
|
txHash,
|
||||||
expectedAmount,
|
new BN(expectedTokenAmount),
|
||||||
paymentMethod,
|
paymentMethod,
|
||||||
expectedRecipientAddress
|
expectedRecipientAddress
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,7 @@ interface WalletProvidersProps {
|
|||||||
|
|
||||||
export default function WalletProviders({ children }: WalletProvidersProps) {
|
export default function WalletProviders({ children }: WalletProvidersProps) {
|
||||||
const { selectedPaymentMethod } = usePaymentMethod();
|
const { selectedPaymentMethod } = usePaymentMethod();
|
||||||
|
|
||||||
// Configure the Solana network endpoint
|
// Configure the Solana network endpoint
|
||||||
const endpoint = useMemo(() => {
|
const endpoint = useMemo(() => {
|
||||||
return SOLANA_RPC_URL;
|
return SOLANA_RPC_URL;
|
||||||
@ -46,7 +46,7 @@ export default function WalletProviders({ children }: WalletProvidersProps) {
|
|||||||
|
|
||||||
return allWallets.filter(wallet => {
|
return allWallets.filter(wallet => {
|
||||||
const isBackpack = wallet.name.toLowerCase().includes('backpack');
|
const isBackpack = wallet.name.toLowerCase().includes('backpack');
|
||||||
|
|
||||||
if (selectedPaymentMethod === 'nat-gor') {
|
if (selectedPaymentMethod === 'nat-gor') {
|
||||||
return isBackpack; // Only Backpack for native GOR
|
return isBackpack; // Only Backpack for native GOR
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
// Payment configuration constants
|
// 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
|
// Payment method types
|
||||||
export type PaymentMethod = 'nat-gor' | 'spl-token';
|
export type PaymentMethod = 'nat-gor' | 'spl-token';
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ interface RequiredTokenInfo {
|
|||||||
decimals: number;
|
decimals: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WRAPPED_SOL_MINT_ADDRESS = 'So11111111111111111111111111111111111111112';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches token price from Jupiter aggregator API
|
* Fetches token price from Jupiter aggregator API
|
||||||
* @param mintAddress The Solana token mint address
|
* @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)
|
* @returns The GOR amount in lamports needed and decimals (always 9 for SOL/GOR)
|
||||||
*/
|
*/
|
||||||
export async function getRequiredNativeGorInfo(targetUsdAmount: number): Promise<RequiredTokenInfo> {
|
export async function getRequiredNativeGorInfo(targetUsdAmount: number): Promise<RequiredTokenInfo> {
|
||||||
// Wrapped SOL mint address
|
const priceInfo = await getTokenInfo(WRAPPED_SOL_MINT_ADDRESS);
|
||||||
const wrappedSolMint = 'So11111111111111111111111111111111111111112';
|
|
||||||
|
|
||||||
const priceInfo = await getTokenInfo(wrappedSolMint);
|
|
||||||
|
|
||||||
// Calculate GOR amount needed (same as SOL)
|
// Calculate GOR amount needed (same as SOL)
|
||||||
const gorAmount = targetUsdAmount / priceInfo.usdPrice;
|
const gorAmount = targetUsdAmount / priceInfo.usdPrice;
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
import { WalletAdapter } from '@solana/wallet-adapter-base';
|
import { WalletAdapter } from '@solana/wallet-adapter-base';
|
||||||
|
|
||||||
import { SolanaPaymentResult, PaymentRequest } from '../types';
|
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_MINT_ADDRESS, 'SOLANA_TOKEN_MINT_ADDRESS is required');
|
||||||
assert(process.env.NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS, 'SOLANA_TOKEN_RECIPIENT_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(
|
export async function sendSolPayment(
|
||||||
wallet: WalletAdapter,
|
wallet: WalletAdapter,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
walletPublicKey: string
|
walletPublicKey: string,
|
||||||
|
tokenAmount: BN
|
||||||
): Promise<SolanaPaymentResult> {
|
): Promise<SolanaPaymentResult> {
|
||||||
try {
|
try {
|
||||||
if (!wallet.connected || !wallet.publicKey) {
|
if (!wallet.connected || !wallet.publicKey) {
|
||||||
@ -51,14 +52,14 @@ export async function sendSolPayment(
|
|||||||
console.log('Processing native GOR payment:', {
|
console.log('Processing native GOR payment:', {
|
||||||
sender: senderPublicKey.toBase58(),
|
sender: senderPublicKey.toBase58(),
|
||||||
receiver: receiverPublicKey.toBase58(),
|
receiver: receiverPublicKey.toBase58(),
|
||||||
amount: SOL_PAYMENT_AMOUNT_LAMPORTS
|
amount: tokenAmount.toString()
|
||||||
});
|
});
|
||||||
|
|
||||||
const transaction = new Transaction().add(
|
const transaction = new Transaction().add(
|
||||||
SystemProgram.transfer({
|
SystemProgram.transfer({
|
||||||
fromPubkey: senderPublicKey,
|
fromPubkey: senderPublicKey,
|
||||||
toPubkey: receiverPublicKey,
|
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');
|
throw new Error('Wallet not connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tokenAmount = new BN(paymentRequest.amount);
|
||||||
|
|
||||||
switch (paymentRequest.paymentMethod) {
|
switch (paymentRequest.paymentMethod) {
|
||||||
case 'nat-gor':
|
case 'nat-gor':
|
||||||
return await sendSolPayment(wallet, connection, walletPublicKey);
|
return await sendSolPayment(wallet, connection, walletPublicKey, tokenAmount);
|
||||||
case 'spl-token':
|
case 'spl-token':
|
||||||
const tokenAmount = new BN(paymentRequest.amount);
|
|
||||||
return await sendSplTokenPayment(wallet, connection, walletPublicKey, tokenAmount);
|
return await sendSplTokenPayment(wallet, connection, walletPublicKey, tokenAmount);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported payment method: ${paymentRequest.paymentMethod}`);
|
throw new Error(`Unsupported payment method: ${paymentRequest.paymentMethod}`);
|
||||||
|
@ -5,7 +5,7 @@ import { PaymentMethod } from '../constants/payments';
|
|||||||
|
|
||||||
// Extract transaction info for native GOR transfers
|
// Extract transaction info for native GOR transfers
|
||||||
const extractSolTransferInfo = async (
|
const extractSolTransferInfo = async (
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
transactionSignature: string
|
transactionSignature: string
|
||||||
): Promise<{ authority: string; amount: string; destination: string }> => {
|
): Promise<{ authority: string; amount: string; destination: string }> => {
|
||||||
const result = await connection.getParsedTransaction(transactionSignature, 'confirmed');
|
const result = await connection.getParsedTransaction(transactionSignature, 'confirmed');
|
||||||
@ -29,7 +29,7 @@ const extractSolTransferInfo = async (
|
|||||||
|
|
||||||
// Extract transaction info for SPL token transfers
|
// Extract transaction info for SPL token transfers
|
||||||
const extractSplTokenTransferInfo = async (
|
const extractSplTokenTransferInfo = async (
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
transactionSignature: string
|
transactionSignature: string
|
||||||
): Promise<{ authority: string; amount: string; destination: string }> => {
|
): Promise<{ authority: string; amount: string; destination: string }> => {
|
||||||
const result = await connection.getParsedTransaction(transactionSignature, 'confirmed');
|
const result = await connection.getParsedTransaction(transactionSignature, 'confirmed');
|
||||||
@ -48,14 +48,14 @@ const extractSplTokenTransferInfo = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const parsed = transferInstruction.parsed;
|
const parsed = transferInstruction.parsed;
|
||||||
|
|
||||||
// Handle both transferChecked and transfer types
|
// Handle both transferChecked and transfer types
|
||||||
if (parsed.type === 'transferChecked') {
|
if (parsed.type === 'transferChecked') {
|
||||||
const { info: { tokenAmount, authority, destination } } = parsed;
|
const { info: { tokenAmount, authority, destination } } = parsed;
|
||||||
return {
|
return {
|
||||||
authority,
|
authority,
|
||||||
amount: tokenAmount.amount,
|
amount: tokenAmount.amount,
|
||||||
destination
|
destination
|
||||||
};
|
};
|
||||||
} else if (parsed.type === 'transfer') {
|
} else if (parsed.type === 'transfer') {
|
||||||
const { info: { amount, authority, destination } } = parsed;
|
const { info: { amount, authority, destination } } = parsed;
|
||||||
@ -83,8 +83,6 @@ export const verifyUnusedSolanaPayment = async (
|
|||||||
// Fetch transaction details
|
// Fetch transaction details
|
||||||
const transactionResult = await connection.getParsedTransaction(transactionSignature, 'confirmed');
|
const transactionResult = await connection.getParsedTransaction(transactionSignature, 'confirmed');
|
||||||
|
|
||||||
console.dir(transactionResult, {depth: null});
|
|
||||||
|
|
||||||
if (!transactionResult) {
|
if (!transactionResult) {
|
||||||
return {
|
return {
|
||||||
valid: false,
|
valid: false,
|
||||||
@ -167,7 +165,7 @@ export const verifyUnusedSolanaPayment = async (
|
|||||||
recipientPublicKey,
|
recipientPublicKey,
|
||||||
true // Allow off-curve addresses
|
true // Allow off-curve addresses
|
||||||
);
|
);
|
||||||
|
|
||||||
validRecipient = destination === expectedTokenAccount.toBase58();
|
validRecipient = destination === expectedTokenAccount.toBase58();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user