forked from mito-systems/sol-mem-gen
183 lines
5.0 KiB
TypeScript
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'
|
|
}
|
|
}
|
|
}
|