diff --git a/.env.example b/.env.example index df4e429..df7daba 100644 --- a/.env.example +++ b/.env.example @@ -21,7 +21,8 @@ NEXT_PUBLIC_REGISTRY_CHAIN_ID=laconic-mainnet NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/graphql NEXT_PUBLIC_REGISTRY_GAS_PRICE=0.001 -NEXT_PUBLIC_PRICING_RECORD_LRN= +NEXT_PUBLIC_ALNT_COST_LRN= +NEXT_PUBLIC_DEPLOYMENT_COST_LRN= REGISTRY_BOND_ID= REGISTRY_AUTHORITY= REGISTRY_USER_KEY= diff --git a/src/app/api/registry/route.ts b/src/app/api/registry/route.ts index a65c5b8..7ec48f0 100644 --- a/src/app/api/registry/route.ts +++ b/src/app/api/registry/route.ts @@ -13,7 +13,7 @@ import { getRegistry, getRegistryConfig } from '@/config'; import { getRequiredTokenInfo, RequiredTokenInfo } from '@/services/jupiter-price'; import { IS_NAT_GOR_TRANSFER_ENABLED, SOLANA_GOR_MINT_ADDRESS } from '@/constants/payments'; import { PaymentMethod } from '@/types'; -import { resolvePricingRecordLrn } from '@/services/registry'; +import { getCostOfDeployment } from '@/services/registry'; assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required'); assert(!IS_NAT_GOR_TRANSFER_ENABLED || process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL, 'GORBAGANA_RPC_URL is required when NAT GOR transfer is enabled'); @@ -219,8 +219,7 @@ export async function POST(request: NextRequest) { // Verify Solana payment console.log('Step 0: Verifying Solana token payment...'); let requiredTokenInfo: RequiredTokenInfo; - const pricingRecordAttributes = await resolvePricingRecordLrn(); - const targetUsdAmount = parseInt(pricingRecordAttributes.amount, 10); + const targetUsdAmount = await getCostOfDeployment(); const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!; try { diff --git a/src/components/PaymentModal.tsx b/src/components/PaymentModal.tsx index 47be3e8..be9e330 100644 --- a/src/components/PaymentModal.tsx +++ b/src/components/PaymentModal.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useState, useEffect, useMemo } from 'react'; +import { useCallback, useState, useEffect } from 'react'; import assert from 'assert'; import { Connection } from '@solana/web3.js'; @@ -11,7 +11,7 @@ import { getRequiredTokenInfo, RequiredTokenInfo } from '@/services/jupiter-pric import { PaymentMethod, PaymentModalProps, PaymentRequest } from '@/types'; import { IS_NAT_GOR_TRANSFER_ENABLED, PAYMENT_METHOD_LABELS, SOLANA_GOR_MINT_ADDRESS } from '@/constants/payments'; import { usePaymentMethod } from '@/contexts/PaymentMethodContext'; -import { resolvePricingRecordLrn } from '@/services/registry'; +import { getCostOfDeployment } from '@/services/registry'; assert(!IS_NAT_GOR_TRANSFER_ENABLED || process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL, 'GORBAGANA_RPC_URL is required when NAT GOR transfer is enabled'); @@ -20,11 +20,6 @@ const GORBAGANA_RPC_URL = process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL; assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required'); assert(process.env.NEXT_PUBLIC_PRICING_RECORD_LRN, 'DEPLOYMENT_RECORD_LRN is required'); -interface DeploymentCostInfo { - amount: string; - currency: string; -} - export default function PaymentModal({ isOpen, onClose, @@ -40,30 +35,18 @@ export default function PaymentModal({ const [tokenAmount, setTokenAmount] = useState(0); const [tokenDecimals, setTokenDecimals] = useState(6); // Default fallback const [loadingPrice, setLoadingPrice] = useState(true); - const [deploymentCostInfo, setDeploymentCostInfo] = useState(); + const [deploymentCost, setDeploymentCost] = useState(null); useEffect(() => { const getDeploymentCostInfo = async () => { - const pricingRecordAttributes = await resolvePricingRecordLrn(); + const cost = await getCostOfDeployment(); - setDeploymentCostInfo({ - amount: pricingRecordAttributes.amount, - currency:pricingRecordAttributes.currency - }) + setDeploymentCost(cost); } getDeploymentCostInfo(); }, []); - // Get configuration from environment variables - const deploymentCost = useMemo(() => { - if (!deploymentCostInfo) { - return; - } - - return parseInt(deploymentCostInfo.amount, 10); - }, [deploymentCostInfo]) - const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!; const tokenSymbol = process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL; diff --git a/src/services/registry.ts b/src/services/registry.ts index 861612b..4962d89 100644 --- a/src/services/registry.ts +++ b/src/services/registry.ts @@ -3,12 +3,13 @@ import assert from 'assert'; import { getRegistry } from '@/config'; import { CreateRecordResponse, PricingRecordAttributes, PaymentMethod } from '../types'; -assert(process.env.NEXT_PUBLIC_PRICING_RECORD_LRN, 'DEPLOYMENT_RECORD_LRN is required'); -const PRICING_RECORD_LRN = process.env.NEXT_PUBLIC_PRICING_RECORD_LRN; - -const SUPPORTED_CURRENCY = "USD"; -const VALID_PRICING_RECORD_FOR = "webapp-deployment"; +assert(process.env.NEXT_PUBLIC_DEPLOYMENT_COST_LRN, 'DEPLOYMENT_RECORD_LRN is required'); +assert(process.env.NEXT_PUBLIC_ALNT_COST_LRN, 'DEPLOYMENT_RECORD_LRN is required'); +const DEPLOYMENT_COST_LRN = process.env.NEXT_PUBLIC_DEPLOYMENT_COST_LRN; +const DEPLOYMENT = 'webapp-deployment'; +const ALNT_COST_LRN = process.env.NEXT_PUBLIC_ALNT_COST_LRN; +const ALNT = 'alnt'; export const createApplicationDeploymentRequest = async ( url: string, @@ -60,18 +61,36 @@ export const createApplicationDeploymentRequest = async ( } }; -export const resolvePricingRecordLrn = async (): Promise => { +const resolvePricingRecordLrns = async (lrns: string[]): Promise => { const registry = getRegistry(); - const result = await registry.resolveNames([PRICING_RECORD_LRN]); - const pricingRecordAttributes: PricingRecordAttributes = result[0].attributes; + const result = await registry.resolveNames(lrns); + const pricingRecordsAttributes: PricingRecordAttributes[] = result.map((record: any) => { + return record.attributes + }); - if (pricingRecordAttributes.for !== VALID_PRICING_RECORD_FOR) { - throw new Error(`Incorrect pricing record type: ${pricingRecordAttributes.type}. Please provide correct pricing record lrn`) + return pricingRecordsAttributes; +}; + +export const getCostOfDeployment = async (): Promise => { + const resolvedRecords = await resolvePricingRecordLrns([ALNT_COST_LRN, DEPLOYMENT_COST_LRN]); + console.log('resolvedRecords:', resolvedRecords); + + // Find the ALNT price record (USD per ALNT) + const alntPriceRecord = resolvedRecords.find(record => record.for === ALNT); + // Find the deployment cost record (ALNT cost for webapp-deployment) + const deploymentCostRecord = resolvedRecords.find(record => record.for === DEPLOYMENT); + + if (!alntPriceRecord || !deploymentCostRecord) { + throw new Error('Required pricing records not found'); } - if (pricingRecordAttributes.currency !== SUPPORTED_CURRENCY) { - throw new Error(`Unsupported currency found in pricing record: ${pricingRecordAttributes.currency}`) - } + // Convert strings to numbers for calculation + const alntPriceUsd = parseFloat(alntPriceRecord.amount); // USD per ALNT + const deploymentCostAlnt = parseFloat(deploymentCostRecord.amount); // ALNT required - return pricingRecordAttributes; + // Calculate deployment cost in USD: (ALNT required) * (USD per ALNT) + const deploymentCostUsd = deploymentCostAlnt * alntPriceUsd; + + // Return with 6 decimal precision + return parseFloat(deploymentCostUsd.toFixed(6)); }