Use big.js for handling decimals

This commit is contained in:
Adw8 2025-01-30 14:41:29 +05:30
parent cc64e6a035
commit 2e735c51dd
9 changed files with 39 additions and 19 deletions

14
package-lock.json generated
View File

@ -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",
"big.js": "^6.2.2",
"bn.js": "^5.2.0", "bn.js": "^5.2.0",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"next": "13.5.4", "next": "13.5.4",
@ -1501,6 +1502,19 @@
} }
] ]
}, },
"node_modules/big.js": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz",
"integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==",
"license": "MIT",
"engines": {
"node": "*"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/bigjs"
}
},
"node_modules/bigint-buffer": { "node_modules/bigint-buffer": {
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz",

View File

@ -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",
"big.js": "^6.2.2",
"bn.js": "^5.2.0", "bn.js": "^5.2.0",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"next": "13.5.4", "next": "13.5.4",

View File

@ -1,6 +1,7 @@
import assert from "assert"; import assert from "assert";
import BN from "bn.js"; import BN from "bn.js";
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import Big from 'big.js';
assert(process.env.NEXT_PUBLIC_USDC_MINT, 'USDC_MINT is required'); assert(process.env.NEXT_PUBLIC_USDC_MINT, 'USDC_MINT 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');
@ -21,14 +22,15 @@ class QuotesService {
} }
const quoteResponse = await response.json(); const quoteResponse = await response.json();
const priceFromAPI = Number(quoteResponse['data'][USDC_MINT]['price']).toFixed(6);
const price = new BN(priceFromAPI.toString().replace('.', '')); // Handle price with Big.js, then convert to BN
const priceFromAPI = new Big(quoteResponse['data'][USDC_MINT]['price']).toFixed(6);
const price = new BN(new Big(priceFromAPI).times(new Big(10).pow(6)).toString());
this.cachedQuotes.push(price); this.cachedQuotes.push(price);
if (this.cachedQuotes.length > 3) { if (this.cachedQuotes.length > 3) {
this.cachedQuotes.shift(); this.cachedQuotes.shift();
} }
console.log('Cache updated: ', this.cachedQuotes);
} catch (error) { } catch (error) {
console.error('Error fetching quotes:', error); console.error('Error fetching quotes:', error);
} }

View File

@ -1,6 +1,8 @@
import { createServer } from 'http'; import { createServer } from 'http';
import { parse } from 'url'; import { parse } from 'url';
import next from 'next'; import next from 'next';
// Reference: https://www.dotenv.org/docs/quickstart#initial-setup
import dotenv from 'dotenv'; import dotenv from 'dotenv';
dotenv.config(); dotenv.config();
@ -16,9 +18,9 @@ declare global {
namespace NodeJS { namespace NodeJS {
interface Global { interface Global {
quotesService: typeof quotesService quotesService: typeof quotesService
}
} }
} }
}
// TODO: Look for a better way to use quotesService // TODO: Look for a better way to use quotesService
// Initialize global quotes service // Initialize global quotes service

View File

@ -48,13 +48,13 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
} }
const quotes: BN[] = (global as any).quotesService.getQuotes(); const quotes: BN[] = (global as any).quotesService.getQuotes();
const priceMTMFor1USDC = quotes.reduce((min, quote) => quote.lt(min) ? quote : min, quotes[0]); // Take the minimum quote from the array const lowestQuote = quotes.reduce((minQuote, currentQuote) => BN.min(minQuote, currentQuote), quotes[0]);
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 lowestQuote = scaledCost.mul(new BN(priceMTMFor1USDC)).div(scale); const tokenAmount = scaledCost.mul(new BN(lowestQuote)).div(scale);
const isPaymentVerified = await verifyPayment(transactionSignature, lowestQuote); const isPaymentVerified = await verifyPayment(transactionSignature, tokenAmount);
if (!isPaymentVerified) { if (!isPaymentVerified) {
return NextResponse.json( return NextResponse.json(

View File

@ -132,7 +132,6 @@ const Page: React.FC = (): React.ReactElement => {
isWalletConnected={walletState.connected} isWalletConnected={walletState.connected}
onGenerate={handleFluxGeneration( onGenerate={handleFluxGeneration(
model.modelId, model.modelId,
// Calculate scaled cost directly in bn.js
priceMTM priceMTM
)} )}
priceMTM={priceMTM} priceMTM={priceMTM}

View File

@ -2,6 +2,7 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import BN from 'bn.js'; import BN from 'bn.js';
import Big from 'big.js';
interface AIServiceCardProps { interface AIServiceCardProps {
title: string title: string
@ -19,12 +20,12 @@ interface GenerationState {
} }
const formatBNWithDecimals = (value: BN, decimals: number): string => { const formatBNWithDecimals = (value: BN, decimals: number): string => {
const factor = new BN(10).pow(new BN(decimals)); if (value.isZero()) return '0.' + '0'.repeat(decimals); // Handle zero case
// Part before decimal point
const quotient = value.div(factor); const bigValue = new Big(value.toString());
// Part after decimal point const factor = new Big(10).pow(decimals);
const remainder = value.mod(factor);
return `${quotient.toString()}.${remainder.toString().padStart(decimals, '0')}`; return bigValue.div(factor).toFixed(decimals);
} }
const AIServiceCard: React.FC<AIServiceCardProps> = ({ const AIServiceCard: React.FC<AIServiceCardProps> = ({
@ -64,6 +65,8 @@ const AIServiceCard: React.FC<AIServiceCardProps> = ({
setTimeout(() => { setTimeout(() => {
window.location.reload(); window.location.reload();
}, 3000); }, 3000);
return
} }
if (result.imageUrl) { if (result.imageUrl) {
@ -124,7 +127,7 @@ const AIServiceCard: React.FC<AIServiceCardProps> = ({
{generationState.error && ( {generationState.error && (
<div className="mt-4 bg-red-900/20 border border-red-500/20 text-red-400 px-4 py-3 rounded-xl text-center"> <div className="mt-4 bg-red-900/20 border border-red-500/20 text-red-400 px-4 py-3 rounded-xl text-center">
{generationState.error}, reloading... {generationState.error}
</div> </div>
)} )}

View File

@ -51,7 +51,7 @@ export async function generateWithFlux(
}) })
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to generate image') throw new Error('Failed to generate image, reloading...')
} }
const data = await response.json() const data = await response.json()

View File

@ -47,7 +47,7 @@ export async function markSignatureAsUsed(transactionSignature: string): Promise
export async function verifyPayment( export async function verifyPayment(
transactionSignature: string, transactionSignature: string,
lowestQuote: BN, tokenAmount: BN,
): Promise<boolean> { ): Promise<boolean> {
try { try {
// Check if the signature is already used // Check if the signature is already used
@ -74,8 +74,7 @@ export async function verifyPayment(
const transactionAmount = new BN(amount); const transactionAmount = new BN(amount);
if (transactionAmount.gte(lowestQuote)) { if (transactionAmount.gte(tokenAmount)) {
console.log('Transaction amount is greater than minimum amount');
return true; return true;
} }
console.log('Transaction amount is less than minimum amount. Rejecting request'); console.log('Transaction amount is less than minimum amount. Rejecting request');