forked from mito-systems/sol-mem-gen
Use backend cache for getting quotes during payment verification
This commit is contained in:
parent
f712adfec2
commit
ddda589da9
@ -34,9 +34,8 @@ class QuotesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatestQuote(): BN | undefined {
|
getQuotes(): BN[] {
|
||||||
console.log({ cachedQuotes: this.cachedQuotes });
|
return this.cachedQuotes;
|
||||||
return this.cachedQuotes[this.cachedQuotes.length - 1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,7 @@ import BN from 'bn.js';
|
|||||||
import { fal } from "@fal-ai/client"
|
import { fal } from "@fal-ai/client"
|
||||||
import { FLUX_MODELS } from '../../../services/fluxService'
|
import { FLUX_MODELS } from '../../../services/fluxService'
|
||||||
import { initializeDataSource } from '../../../data-source'
|
import { initializeDataSource } from '../../../data-source'
|
||||||
|
import { verifyPayment, markSignatureAsUsed } from '../../../utils/verifyPayment';
|
||||||
import { verifyPayment, markSignatureAsUsed } from '../../../utils/verifyPayment'
|
|
||||||
import { getUSDCToMTMQuote } from '../../../services/paymentService'
|
|
||||||
|
|
||||||
if (!process.env.FAL_AI_KEY) {
|
if (!process.env.FAL_AI_KEY) {
|
||||||
throw new Error('FAL_AI_KEY is not configured in environment variables')
|
throw new Error('FAL_AI_KEY is not configured in environment variables')
|
||||||
@ -49,17 +47,18 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mtmFor1USDC = await getUSDCToMTMQuote();
|
const quotes: BN[] = (global as any).quotesService.getQuotes();
|
||||||
|
const mtmFor1USDC = quotes.reduce((min, quote) => quote.lt(min) ? quote : min, quotes[0]); // Take the least cost in the array
|
||||||
|
|
||||||
const scale = new BN(100);
|
const scale = new BN(100);
|
||||||
const scaledCost = new BN(model.cost * 100);
|
const scaledCost = new BN(model.cost * 100);
|
||||||
|
|
||||||
const expectedAmount = scaledCost.mul(mtmFor1USDC).div(scale);
|
const lowestQuote = scaledCost.mul(new BN(mtmFor1USDC)).div(scale);
|
||||||
const isPaymentVerified = await verifyPayment(transactionSignature, expectedAmount)
|
const isPaymentVerified = await verifyPayment(transactionSignature, lowestQuote);
|
||||||
|
|
||||||
if (!isPaymentVerified) {
|
if (!isPaymentVerified) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'Payment verification failed or transaction signature has already been used' },
|
{ error: 'Payment verification failed or transaction signature has already been used', reload: true },
|
||||||
{ status: 400 }
|
{ status: 400 }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
|
import BN from 'bn.js';
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
const quote = (global as any).quoteService.getLatestQuote();
|
try {
|
||||||
return NextResponse.json(quote);
|
const quotes: BN[] = (global as any).quotesService.getQuotes();
|
||||||
|
const quotesAsString = quotes.map(quote => quote.toString());
|
||||||
|
return NextResponse.json(quotesAsString);
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json({ error: 'Failed to fetch quotes' }, { status: 500 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,10 @@ const Page: React.FC = (): React.ReactElement => {
|
|||||||
try {
|
try {
|
||||||
const response = await fetch('/api/quotes');
|
const response = await fetch('/api/quotes');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setMtmFor1USDC(new BN(data.latestQuote)); // Convert the string back to BN
|
|
||||||
|
// Convert the string back to BN
|
||||||
|
const price = new BN(data[data.length - 1]);
|
||||||
|
setMtmFor1USDC(price);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch price:', error);
|
console.error('Failed to fetch price:', error);
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,8 @@ export async function generateWithFlux(
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Flux generation error:', error)
|
console.error('Flux generation error:', error)
|
||||||
|
// Reload the page to get latest prices
|
||||||
|
window.location.reload();
|
||||||
return {
|
return {
|
||||||
error: error instanceof Error ? error.message : 'Generation failed'
|
error: error instanceof Error ? error.message : 'Generation failed'
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,11 @@ import { WalletType } from './types'
|
|||||||
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');
|
||||||
assert(process.env.NEXT_PUBLIC_MTM_TOKEN_MINT, 'MTM_TOKEN_MINT 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');
|
assert(process.env.NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS, 'PAYMENT_RECEIVER_ADDRESS is required');
|
||||||
assert(process.env.NEXT_PUBLIC_USDC_MINT, 'USDC_MINT is required');
|
|
||||||
|
|
||||||
const MTM_TOKEN_MINT = process.env.NEXT_PUBLIC_MTM_TOKEN_MINT;
|
const MTM_TOKEN_MINT = process.env.NEXT_PUBLIC_MTM_TOKEN_MINT;
|
||||||
const PAYMENT_RECEIVER_ADDRESS = process.env.NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS;
|
const PAYMENT_RECEIVER_ADDRESS = process.env.NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS;
|
||||||
const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL;
|
const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL;
|
||||||
const SOLANA_WEBSOCKET_URL = process.env.NEXT_PUBLIC_SOLANA_WEBSOCKET_URL;
|
const SOLANA_WEBSOCKET_URL = process.env.NEXT_PUBLIC_SOLANA_WEBSOCKET_URL;
|
||||||
const USDC_MINT = process.env.NEXT_PUBLIC_USDC_MINT;
|
|
||||||
|
|
||||||
const connection = new Connection(
|
const connection = new Connection(
|
||||||
SOLANA_RPC_URL,
|
SOLANA_RPC_URL,
|
||||||
@ -51,22 +49,6 @@ async function findAssociatedTokenAddress(
|
|||||||
)[0]
|
)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUSDCToMTMQuote(): Promise<BN> {
|
|
||||||
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 priceFromAPI = Number(quoteResponse['data'][USDC_MINT]['price']).toFixed(6);
|
|
||||||
const price = new BN(priceFromAPI.toString().replace('.', ''));
|
|
||||||
return price;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WalletAdapter {
|
interface WalletAdapter {
|
||||||
signAndSendTransaction(transaction: Transaction): Promise<{ signature: string }>
|
signAndSendTransaction(transaction: Transaction): Promise<{ signature: string }>
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ export async function markSignatureAsUsed(transactionSignature: string): Promise
|
|||||||
|
|
||||||
export async function verifyPayment(
|
export async function verifyPayment(
|
||||||
transactionSignature: string,
|
transactionSignature: string,
|
||||||
expectedAmount: BN,
|
lowestQuote: BN,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// Check if the signature is already used
|
// Check if the signature is already used
|
||||||
@ -74,17 +74,11 @@ export async function verifyPayment(
|
|||||||
|
|
||||||
const transactionAmount = new BN(amount);
|
const transactionAmount = new BN(amount);
|
||||||
|
|
||||||
// Transaction amount should be within 10% of the expected amount
|
if (transactionAmount.gte(lowestQuote)) {
|
||||||
const lowerBound = expectedAmount.mul(new BN(9)).div(new BN(10));
|
|
||||||
const upperBound = expectedAmount.mul(new BN(11)).div(new BN(10));
|
|
||||||
|
|
||||||
// transaction within the appropriate range
|
|
||||||
if (transactionAmount.gte(lowerBound) && transactionAmount.lte(upperBound)) {
|
|
||||||
console.log('Transaction amount is within the expected range.');
|
console.log('Transaction amount is within the expected range.');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
console.log('Transaction amount is not within the expected range.');
|
console.log('Transaction amount is not within the expected range.');
|
||||||
// TODO: Handle slippage being greater than 10%
|
|
||||||
return false;
|
return false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Verification error:', error);
|
console.error('Verification error:', error);
|
||||||
|
Loading…
Reference in New Issue
Block a user