sol-mem-gen/src/services/paymentService.ts

183 lines
5.0 KiB
TypeScript

import assert from 'assert';
import { Connection, PublicKey, Transaction } from '@solana/web3.js'
import {
TOKEN_PROGRAM_ID,
createTransferInstruction,
createAssociatedTokenAccountInstruction,
ASSOCIATED_TOKEN_PROGRAM_ID
} from '@solana/spl-token'
import { WalletType } from './types'
assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required');
assert(process.env.NEXT_PUBLIC_MTM_TOKEN_MINT, 'MTM_TOKEN_MINT is required');
assert(process.env.NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS, 'PAYMENT_RECEIVER_ADDRESS is required');
const MTM_TOKEN_MINT = process.env.NEXT_PUBLIC_MTM_TOKEN_MINT;
const PAYMENT_RECEIVER_ADDRESS = process.env.NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS;
const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL;
const SOLANA_WEBSOCKET_URL = process.env.NEXT_PUBLIC_SOLANA_WEBSOCKET_URL;
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; // USDC mint address
const connection = new Connection(
SOLANA_RPC_URL,
{
commitment: 'confirmed',
wsEndpoint: SOLANA_WEBSOCKET_URL,
confirmTransactionInitialTimeout: 60000,
}
)
export interface PaymentResult {
success: boolean
transactionSignature?: string
error?: string
}
async function findAssociatedTokenAddress(
walletAddress: PublicKey,
tokenMintAddress: PublicKey
): Promise<PublicKey> {
return PublicKey.findProgramAddressSync(
[
walletAddress.toBuffer(),
TOKEN_PROGRAM_ID.toBuffer(),
tokenMintAddress.toBuffer(),
],
ASSOCIATED_TOKEN_PROGRAM_ID
)[0]
}
export async function getUSDCToMTMQuote() {
const url = `https://api.jup.ag/price/v2?ids=${USDC_MINT}&vsToken=${MTM_TOKEN_MINT}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch quote: ${response.statusText}`);
}
const quoteResponse = await response.json();
const price = quoteResponse['data'][USDC_MINT]['price']
return price;
}
interface WalletAdapter {
signAndSendTransaction(transaction: Transaction): Promise<{ signature: string }>
}
export async function processMTMPayment(
walletPublicKey: string,
tokenAmount: number,
walletType: WalletType
): Promise<PaymentResult> {
try {
let wallet: WalletAdapter | null = null;
if (walletType === 'phantom') {
wallet = window.phantom?.solana || null;
} else if (walletType === 'solflare') {
wallet = window.solflare || null;
}
if (!wallet) {
throw new Error(`${walletType} wallet not found`)
}
const senderPublicKey = new PublicKey(walletPublicKey)
const mintPublicKey = new PublicKey(MTM_TOKEN_MINT)
const receiverPublicKey = new PublicKey(PAYMENT_RECEIVER_ADDRESS)
console.log('Processing payment with keys:', {
sender: senderPublicKey.toBase58(),
mint: mintPublicKey.toBase58(),
receiver: receiverPublicKey.toBase58(),
})
const senderATA = await findAssociatedTokenAddress(
senderPublicKey,
mintPublicKey
)
const receiverATA = await findAssociatedTokenAddress(
receiverPublicKey,
mintPublicKey
)
console.log('Token accounts:', {
senderATA: senderATA.toBase58(),
receiverATA: receiverATA.toBase58(),
})
const transaction = new Transaction()
const [senderATAInfo, receiverATAInfo] = await Promise.all([
connection.getAccountInfo(senderATA),
connection.getAccountInfo(receiverATA),
])
if (!receiverATAInfo) {
console.log('Creating receiver token account')
transaction.add(
createAssociatedTokenAccountInstruction(
senderPublicKey,
receiverATA,
receiverPublicKey,
mintPublicKey
)
)
}
if (!senderATAInfo) {
console.log('Creating sender token account')
transaction.add(
createAssociatedTokenAccountInstruction(
senderPublicKey,
senderATA,
senderPublicKey,
mintPublicKey
)
)
}
transaction.add(
createTransferInstruction(
senderATA,
receiverATA,
senderPublicKey,
BigInt(tokenAmount * (10 ** 6))
)
)
const latestBlockhash = await connection.getLatestBlockhash('confirmed')
transaction.recentBlockhash = latestBlockhash.blockhash
transaction.feePayer = senderPublicKey
console.log('Sending transaction...')
const { signature } = await wallet.signAndSendTransaction(transaction)
console.log('Transaction sent:', signature)
const confirmation = await connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
}, 'confirmed')
if (confirmation.value.err) {
console.error('Transaction error:', confirmation.value.err)
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`)
}
return { success: true, transactionSignature: signature };
} catch (error) {
console.error('Payment error:', error)
return {
success: false,
error: error instanceof Error ? error.message : 'Payment failed'
}
}
}