forked from mito-systems/sol-mem-gen
Use bn.js to handle big numbers
This commit is contained in:
parent
8b5dbb46fa
commit
53bd18f031
1
package-lock.json
generated
1
package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"@google/generative-ai": "^0.21.0",
|
"@google/generative-ai": "^0.21.0",
|
||||||
"@solana/spl-token": "^0.3.8",
|
"@solana/spl-token": "^0.3.8",
|
||||||
"@solana/web3.js": "^1.78.4",
|
"@solana/web3.js": "^1.78.4",
|
||||||
|
"bn.js": "^5.2.0",
|
||||||
"next": "13.5.4",
|
"next": "13.5.4",
|
||||||
"openai": "^4.77.0",
|
"openai": "^4.77.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
"@google/generative-ai": "^0.21.0",
|
"@google/generative-ai": "^0.21.0",
|
||||||
"@solana/spl-token": "^0.3.8",
|
"@solana/spl-token": "^0.3.8",
|
||||||
"@solana/web3.js": "^1.78.4",
|
"@solana/web3.js": "^1.78.4",
|
||||||
|
"bn.js": "^5.2.0",
|
||||||
"next": "13.5.4",
|
"next": "13.5.4",
|
||||||
"openai": "^4.77.0",
|
"openai": "^4.77.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server'
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
|
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'
|
||||||
@ -49,11 +51,10 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
|||||||
|
|
||||||
const MTMFor1USDC = await getUSDCToMTMQuote();
|
const MTMFor1USDC = await getUSDCToMTMQuote();
|
||||||
|
|
||||||
// TODO: Use bn.js for better precision
|
const scale = new BN(100);
|
||||||
const scale = BigInt(100);
|
const scaledCost = new BN(model.cost * 100);
|
||||||
const scaledCost = BigInt(Math.round(model.cost * Number(scale)));
|
|
||||||
|
|
||||||
const expectedAmount = (scaledCost * MTMFor1USDC) / scale;
|
const expectedAmount = scaledCost.mul(MTMFor1USDC).div(scale);
|
||||||
const isPaymentVerified = await verifyPayment(transactionSignature, expectedAmount)
|
const isPaymentVerified = await verifyPayment(transactionSignature, expectedAmount)
|
||||||
|
|
||||||
if (!isPaymentVerified) {
|
if (!isPaymentVerified) {
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
import WalletHeader from '../components/WalletHeader'
|
import WalletHeader from '../components/WalletHeader'
|
||||||
import AIServiceCard from '../components/AIServiceCard'
|
import AIServiceCard from '../components/AIServiceCard'
|
||||||
import { generateWithFlux, FluxGenerationResult, FLUX_MODELS } from '../services/fluxService'
|
import { generateWithFlux, FluxGenerationResult, FLUX_MODELS } from '../services/fluxService'
|
||||||
@ -15,7 +17,7 @@ const Page: React.FC = (): React.ReactElement => {
|
|||||||
type: null
|
type: null
|
||||||
})
|
})
|
||||||
|
|
||||||
const [MTMFor1USDC, setMTMFor1USDC] = useState<bigint>(BigInt(0));
|
const [MTMFor1USDC, setMTMFor1USDC] = useState<BN>(new BN(0));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchPrice = async () => {
|
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<FluxGenerationResult> => {
|
return async (prompt: string): Promise<FluxGenerationResult> => {
|
||||||
const { connected, publicKey, type } = walletState;
|
const { connected, publicKey, type } = walletState;
|
||||||
|
|
||||||
@ -111,36 +113,31 @@ const Page: React.FC = (): React.ReactElement => {
|
|||||||
walletState={walletState}
|
walletState={walletState}
|
||||||
onConnect={handleConnect}
|
onConnect={handleConnect}
|
||||||
onDisconnect={handleDisconnect}
|
onDisconnect={handleDisconnect}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Flux Models Grid */}
|
{/* Flux Models Grid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||||
{FLUX_MODELS.map((model) => (
|
{FLUX_MODELS.map((model) => {
|
||||||
<AIServiceCard
|
// Convert cost from number to BN
|
||||||
key={model.modelId}
|
const costBN = new BN(model.cost * 100);
|
||||||
title={model.name}
|
const mtmPriceBN = costBN.mul(MTMFor1USDC);
|
||||||
description={model.description}
|
|
||||||
isWalletConnected={walletState.connected}
|
|
||||||
// TODO: Use bn.js for better precision
|
|
||||||
onGenerate={handleFluxGeneration(
|
|
||||||
model.modelId,
|
|
||||||
(() => {
|
|
||||||
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;
|
|
||||||
})()}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AIServiceCard
|
||||||
|
key={model.modelId}
|
||||||
|
title={model.name}
|
||||||
|
description={model.description}
|
||||||
|
isWalletConnected={walletState.connected}
|
||||||
|
onGenerate={handleFluxGeneration(
|
||||||
|
model.modelId,
|
||||||
|
// Calculate scaled cost directly in bn.js
|
||||||
|
mtmPriceBN.div(new BN(100))
|
||||||
|
)}
|
||||||
|
mtmPrice={mtmPriceBN.div(new BN(100))}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{/* Coming Soon Card */}
|
{/* Coming Soon Card */}
|
||||||
<div className="relative bg-gray-800/50 backdrop-blur-lg rounded-2xl shadow-xl border border-gray-700/50 overflow-hidden group">
|
<div className="relative bg-gray-800/50 backdrop-blur-lg rounded-2xl shadow-xl border border-gray-700/50 overflow-hidden group">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-yellow-500/10 to-orange-500/10 opacity-50"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-yellow-500/10 to-orange-500/10 opacity-50"></div>
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
interface AIServiceCardProps {
|
interface AIServiceCardProps {
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
isWalletConnected: boolean
|
isWalletConnected: boolean
|
||||||
onGenerate: (prompt: string) => Promise<{ imageUrl?: string, error?: string }>
|
onGenerate: (prompt: string) => Promise<{ imageUrl?: string, error?: string }>
|
||||||
mtmPrice: bigint
|
mtmPrice: BN
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GenerationState {
|
interface GenerationState {
|
||||||
@ -81,7 +82,7 @@ const AIServiceCard: React.FC<AIServiceCardProps> = ({
|
|||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-400 mt-2">{description}</p>
|
<p className="text-gray-400 mt-2">{description}</p>
|
||||||
<div className="mt-2 inline-block px-3 py-1 bg-green-500/20 rounded-full text-green-300 text-sm">
|
<div className="mt-2 inline-block px-3 py-1 bg-green-500/20 rounded-full text-green-300 text-sm">
|
||||||
Cost: {mtmPrice ? (mtmPrice / BigInt(10 ** 6)).toString() : '...'} MTM
|
Cost: {mtmPrice ? mtmPrice.div(new BN(10).pow(new BN(6))).toString() : '...'} MTM
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ const AIServiceCard: React.FC<AIServiceCardProps> = ({
|
|||||||
transition-all duration-200 shadow-lg hover:shadow-green-500/25
|
transition-all duration-200 shadow-lg hover:shadow-green-500/25
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:shadow-none"
|
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`}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
import { Connection, PublicKey, Transaction } from '@solana/web3.js'
|
import { Connection, PublicKey, Transaction } from '@solana/web3.js'
|
||||||
import {
|
import {
|
||||||
@ -50,7 +51,7 @@ async function findAssociatedTokenAddress(
|
|||||||
)[0]
|
)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUSDCToMTMQuote() {
|
export async function getUSDCToMTMQuote(): Promise<BN> {
|
||||||
const url = `https://api.jup.ag/price/v2?ids=${USDC_MINT}&vsToken=${MTM_TOKEN_MINT}`;
|
const url = `https://api.jup.ag/price/v2?ids=${USDC_MINT}&vsToken=${MTM_TOKEN_MINT}`;
|
||||||
|
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
@ -61,9 +62,8 @@ export async function getUSDCToMTMQuote() {
|
|||||||
|
|
||||||
const quoteResponse = await response.json();
|
const quoteResponse = await response.json();
|
||||||
|
|
||||||
// TODO: Use bn.js for better precision
|
const priceFromAPI = Number(quoteResponse['data'][USDC_MINT]['price']).toFixed(6);
|
||||||
const priceFromAPI = Number(quoteResponse['data'][USDC_MINT]['price'])* (10 ** 6);
|
const price = new BN(priceFromAPI.toString().replace('.', ''));
|
||||||
const price = BigInt(Number(priceFromAPI.toFixed(0)));
|
|
||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ interface WalletAdapter {
|
|||||||
|
|
||||||
export async function processMTMPayment(
|
export async function processMTMPayment(
|
||||||
walletPublicKey: string,
|
walletPublicKey: string,
|
||||||
tokenAmount: bigint,
|
tokenAmount: BN,
|
||||||
walletType: WalletType
|
walletType: WalletType
|
||||||
): Promise<PaymentResult> {
|
): Promise<PaymentResult> {
|
||||||
try {
|
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(
|
transaction.add(
|
||||||
createTransferInstruction(
|
createTransferInstruction(
|
||||||
senderATA,
|
senderATA,
|
||||||
receiverATA,
|
receiverATA,
|
||||||
senderPublicKey,
|
senderPublicKey,
|
||||||
tokens * BigInt(10 ** 6)
|
tokens.mul(new BN(10).pow(new BN(6)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
import { Connection } from '@solana/web3.js';
|
import { Connection } from '@solana/web3.js';
|
||||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
||||||
@ -46,7 +47,7 @@ export async function markSignatureAsUsed(transactionSignature: string): Promise
|
|||||||
|
|
||||||
export async function verifyPayment(
|
export async function verifyPayment(
|
||||||
transactionSignature: string,
|
transactionSignature: string,
|
||||||
expectedAmount: bigint,
|
expectedAmount: BN,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// Check if the signature is already used
|
// Check if the signature is already used
|
||||||
@ -71,17 +72,21 @@ export async function verifyPayment(
|
|||||||
const { info } = parsed;
|
const { info } = parsed;
|
||||||
const { amount } = info;
|
const { amount } = info;
|
||||||
|
|
||||||
const transactionAmount = BigInt(amount);
|
console.log('Parsed transfer instruction:', parsed);
|
||||||
const expectedAmountInMicroTokens = expectedAmount;
|
console.log('Transfer info:', info);
|
||||||
|
console.log('Transfer amount:', amount);
|
||||||
|
|
||||||
const lowerBound = expectedAmountInMicroTokens * BigInt(9) / BigInt(10);
|
const transactionAmount = new BN(amount);
|
||||||
const upperBound = expectedAmountInMicroTokens * BigInt(11) / BigInt(10);
|
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
|
// 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;
|
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;
|
return false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Verification error:', error);
|
console.error('Verification error:', error);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user