diff --git a/package-lock.json b/package-lock.json index d46cd39..e12ba2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@google/generative-ai": "^0.21.0", "@solana/spl-token": "^0.3.8", "@solana/web3.js": "^1.78.4", + "bn.js": "^5.2.0", "next": "13.5.4", "openai": "^4.77.0", "react": "^18", diff --git a/package.json b/package.json index c1eeba9..d0b5887 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@google/generative-ai": "^0.21.0", "@solana/spl-token": "^0.3.8", "@solana/web3.js": "^1.78.4", + "bn.js": "^5.2.0", "next": "13.5.4", "openai": "^4.77.0", "react": "^18", diff --git a/src/app/api/flux/route.ts b/src/app/api/flux/route.ts index 4e1a52d..4651870 100644 --- a/src/app/api/flux/route.ts +++ b/src/app/api/flux/route.ts @@ -1,4 +1,6 @@ import { NextRequest, NextResponse } from 'next/server' +import BN from 'bn.js'; + import { fal } from "@fal-ai/client" import { FLUX_MODELS } from '../../../services/fluxService' import { initializeDataSource } from '../../../data-source' @@ -49,11 +51,10 @@ export async function POST(req: NextRequest): Promise { const MTMFor1USDC = await getUSDCToMTMQuote(); - // TODO: Use bn.js for better precision - const scale = BigInt(100); - const scaledCost = BigInt(Math.round(model.cost * Number(scale))); + const scale = new BN(100); + const scaledCost = new BN(model.cost * 100); - const expectedAmount = (scaledCost * MTMFor1USDC) / scale; + const expectedAmount = scaledCost.mul(MTMFor1USDC).div(scale); const isPaymentVerified = await verifyPayment(transactionSignature, expectedAmount) if (!isPaymentVerified) { diff --git a/src/app/page.tsx b/src/app/page.tsx index e462742..88e94c2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,8 @@ 'use client' import React, { useState, useEffect } from 'react' +import BN from 'bn.js'; + import WalletHeader from '../components/WalletHeader' import AIServiceCard from '../components/AIServiceCard' import { generateWithFlux, FluxGenerationResult, FLUX_MODELS } from '../services/fluxService' @@ -15,7 +17,7 @@ const Page: React.FC = (): React.ReactElement => { type: null }) - const [MTMFor1USDC, setMTMFor1USDC] = useState(BigInt(0)); + const [MTMFor1USDC, setMTMFor1USDC] = useState(new BN(0)); useEffect(() => { const fetchPrice = async () => { @@ -58,7 +60,7 @@ const Page: React.FC = (): React.ReactElement => { } }; - const handleFluxGeneration = (modelId: string, cost: bigint) => { + const handleFluxGeneration = (modelId: string, cost: BN) => { return async (prompt: string): Promise => { const { connected, publicKey, type } = walletState; @@ -111,36 +113,31 @@ const Page: React.FC = (): React.ReactElement => { walletState={walletState} onConnect={handleConnect} onDisconnect={handleDisconnect} - /> + /> {/* Flux Models Grid */}
- {FLUX_MODELS.map((model) => ( - { - const scale = BigInt(100); // Scaling factor - const scaledCost = BigInt(Math.round(model.cost * Number(scale))); - const scaledResult = (scaledCost * MTMFor1USDC) / scale; - return scaledResult; - })() - )} - // TODO: Use bn.js for better precision - mtmPrice={(() => { - const scale = BigInt(100); - const scaledCost = BigInt(Math.round(model.cost * Number(scale))); - return (scaledCost * MTMFor1USDC) / scale; - })()} - /> - ))} + {FLUX_MODELS.map((model) => { + // Convert cost from number to BN + const costBN = new BN(model.cost * 100); + const mtmPriceBN = costBN.mul(MTMFor1USDC); + return ( + + ); + })} {/* Coming Soon Card */}
diff --git a/src/components/AIServiceCard.tsx b/src/components/AIServiceCard.tsx index c0910c5..2745663 100644 --- a/src/components/AIServiceCard.tsx +++ b/src/components/AIServiceCard.tsx @@ -1,13 +1,14 @@ 'use client' import React, { useState } from 'react' +import BN from 'bn.js'; interface AIServiceCardProps { title: string description: string isWalletConnected: boolean onGenerate: (prompt: string) => Promise<{ imageUrl?: string, error?: string }> - mtmPrice: bigint + mtmPrice: BN } interface GenerationState { @@ -81,7 +82,7 @@ const AIServiceCard: React.FC = ({

{description}

- Cost: {mtmPrice ? (mtmPrice / BigInt(10 ** 6)).toString() : '...'} MTM + Cost: {mtmPrice ? mtmPrice.div(new BN(10).pow(new BN(6))).toString() : '...'} MTM
@@ -105,7 +106,7 @@ const AIServiceCard: React.FC = ({ transition-all duration-200 shadow-lg hover:shadow-green-500/25 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:shadow-none" > - {generationState.loading ? 'Processing...' : `Pay ${mtmPrice ? (mtmPrice / BigInt(10 ** 6)).toString() : '...'} MTM & Generate`} + {generationState.loading ? 'Processing...' : `Pay ${mtmPrice ? mtmPrice.div(new BN(10).pow(new BN(6))).toString() : '...'} MTM & Generate`}
diff --git a/src/services/paymentService.ts b/src/services/paymentService.ts index dd12f4a..b9eeb95 100644 --- a/src/services/paymentService.ts +++ b/src/services/paymentService.ts @@ -1,4 +1,5 @@ import assert from 'assert'; +import BN from 'bn.js'; import { Connection, PublicKey, Transaction } from '@solana/web3.js' import { @@ -50,7 +51,7 @@ async function findAssociatedTokenAddress( )[0] } -export async function getUSDCToMTMQuote() { +export async function getUSDCToMTMQuote(): Promise { const url = `https://api.jup.ag/price/v2?ids=${USDC_MINT}&vsToken=${MTM_TOKEN_MINT}`; const response = await fetch(url); @@ -61,9 +62,8 @@ export async function getUSDCToMTMQuote() { const quoteResponse = await response.json(); - // TODO: Use bn.js for better precision - const priceFromAPI = Number(quoteResponse['data'][USDC_MINT]['price'])* (10 ** 6); - const price = BigInt(Number(priceFromAPI.toFixed(0))); + const priceFromAPI = Number(quoteResponse['data'][USDC_MINT]['price']).toFixed(6); + const price = new BN(priceFromAPI.toString().replace('.', '')); return price; } @@ -73,7 +73,7 @@ interface WalletAdapter { export async function processMTMPayment( walletPublicKey: string, - tokenAmount: bigint, + tokenAmount: BN, walletType: WalletType ): Promise { try { @@ -145,14 +145,15 @@ export async function processMTMPayment( ) } - const tokens = tokenAmount / BigInt(10 ** 6); + const tokens = tokenAmount.div(new BN(10).pow(new BN(6))); + console.log('TA:', tokenAmount.toString()) transaction.add( createTransferInstruction( senderATA, receiverATA, senderPublicKey, - tokens * BigInt(10 ** 6) + tokens.mul(new BN(10).pow(new BN(6))) ) ) diff --git a/src/utils/verifyPayment.ts b/src/utils/verifyPayment.ts index 0caf559..19547d3 100644 --- a/src/utils/verifyPayment.ts +++ b/src/utils/verifyPayment.ts @@ -1,4 +1,5 @@ import assert from 'assert'; +import BN from 'bn.js'; import { Connection } from '@solana/web3.js'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; @@ -46,7 +47,7 @@ export async function markSignatureAsUsed(transactionSignature: string): Promise export async function verifyPayment( transactionSignature: string, - expectedAmount: bigint, + expectedAmount: BN, ): Promise { try { // Check if the signature is already used @@ -71,17 +72,21 @@ export async function verifyPayment( const { info } = parsed; const { amount } = info; - const transactionAmount = BigInt(amount); - const expectedAmountInMicroTokens = expectedAmount; + console.log('Parsed transfer instruction:', parsed); + console.log('Transfer info:', info); + console.log('Transfer amount:', amount); - const lowerBound = expectedAmountInMicroTokens * BigInt(9) / BigInt(10); - const upperBound = expectedAmountInMicroTokens * BigInt(11) / BigInt(10); + const transactionAmount = new BN(amount); + 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 >= lowerBound && transactionAmount <= upperBound) { + if (transactionAmount.gte(lowerBound) && transactionAmount.lte(upperBound)) { + console.log('Transaction amount is within the expected range.'); return true; } - + console.log('Transaction amount is not within the expected range.'); + // TODO: Handle difference between paid amount and token price during verification being greater than 10% return false; } catch (error) { console.error('Verification error:', error);