forked from mito-systems/sol-mem-gen
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import assert from 'assert';
|
|
import BN from 'bn.js';
|
|
|
|
import { Connection } from '@solana/web3.js';
|
|
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
|
|
import { AppDataSource } from '../data-source';
|
|
import { Payment } from '../entity/Payment';
|
|
|
|
assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required');
|
|
|
|
const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL;
|
|
const SOLANA_WEBSOCKET_URL = process.env.NEXT_PUBLIC_SOLANA_WEBSOCKET_URL;
|
|
|
|
const connection = new Connection(
|
|
SOLANA_RPC_URL,
|
|
{
|
|
commitment: 'confirmed',
|
|
wsEndpoint: SOLANA_WEBSOCKET_URL,
|
|
confirmTransactionInitialTimeout: 60000,
|
|
}
|
|
);
|
|
|
|
export async function isSignatureUsed(transactionSignature: string): Promise<boolean> {
|
|
const paymentRepository = AppDataSource.getRepository(Payment);
|
|
const payment = await paymentRepository.findOneBy({ transactionSignature });
|
|
if (payment) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function markSignatureAsUsed(transactionSignature: string): Promise<void> {
|
|
await AppDataSource.transaction(async (transactionalEntityManager) => {
|
|
const paymentRepository = transactionalEntityManager.getRepository(Payment);
|
|
|
|
// Check if the payment with the given signature already exists
|
|
const exists = await paymentRepository.exists({ where: { transactionSignature } });
|
|
|
|
// If not, create a new payment entry
|
|
if (!exists) {
|
|
const newPayment = paymentRepository.create({ transactionSignature });
|
|
await paymentRepository.save(newPayment);
|
|
}
|
|
});
|
|
}
|
|
|
|
export async function verifyPayment(
|
|
transactionSignature: string,
|
|
tokenAmount: BN,
|
|
): Promise<boolean> {
|
|
try {
|
|
// Check if the signature is already used
|
|
if (await isSignatureUsed(transactionSignature)) {
|
|
return false;
|
|
}
|
|
|
|
const transaction = await connection.getParsedTransaction(transactionSignature, 'confirmed');
|
|
if (!transaction) {
|
|
throw new Error('Transaction not found');
|
|
}
|
|
|
|
const transferInstruction = transaction.transaction.message.instructions.find(
|
|
(instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID)
|
|
);
|
|
|
|
if (!transferInstruction || !('parsed' in transferInstruction)) {
|
|
throw new Error('Transfer instruction not found');
|
|
}
|
|
|
|
const { parsed } = transferInstruction;
|
|
const { info } = parsed;
|
|
const { amount } = info;
|
|
|
|
const transactionAmount = new BN(amount);
|
|
|
|
if (transactionAmount.gte(tokenAmount)) {
|
|
return true;
|
|
}
|
|
console.log('Transaction amount is less than minimum amount. Rejecting request');
|
|
return false;
|
|
} catch (error) {
|
|
console.error('Verification error:', error);
|
|
return false;
|
|
}
|
|
}
|