Take deployment cost from published pricing record

This commit is contained in:
Shreerang Kale 2025-07-22 17:11:39 +05:30
parent 64b6b1082d
commit 6c2ee093a2
5 changed files with 78 additions and 31 deletions

View File

@ -1,4 +1,4 @@
# Client-side environment variables (must be prefixed with NEXT_PUBLIC_) # Client-side environment variables must be prefixed with NEXT_PUBLIC_
# Solana Payment Configuration # Solana Payment Configuration
# TODO: Use different RPC URL or use browser wallet # TODO: Use different RPC URL or use browser wallet
@ -17,12 +17,11 @@ NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS=FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FF
# UI Configuration # UI Configuration
NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app
# Server-side environment variables
# Laconic Registry Configuration # Laconic Registry Configuration
REGISTRY_CHAIN_ID=laconic-mainnet NEXT_PUBLIC_REGISTRY_CHAIN_ID=laconic-mainnet
REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com
REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/graphql NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/graphql
NEXT_PUBLIC_PRICING_RECORD_LRN=
REGISTRY_GAS_PRICE=0.001 REGISTRY_GAS_PRICE=0.001
REGISTRY_BOND_ID= REGISTRY_BOND_ID=
REGISTRY_AUTHORITY= REGISTRY_AUTHORITY=

View File

@ -106,9 +106,9 @@ NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app
### Server-side Variables ### Server-side Variables
```bash ```bash
REGISTRY_CHAIN_ID=laconic-mainnet NEXT_PUBLIC_REGISTRY_CHAIN_ID=laconic-mainnet
REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/api NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/api
REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com
REGISTRY_BOND_ID=<BOND_ID> REGISTRY_BOND_ID=<BOND_ID>
REGISTRY_AUTHORITY=<AUTHORITY_NAME> REGISTRY_AUTHORITY=<AUTHORITY_NAME>
REGISTRY_USER_KEY=<PRIVATE_KEY> REGISTRY_USER_KEY=<PRIVATE_KEY>

View File

@ -284,9 +284,9 @@ export async function POST(request: NextRequest) {
// Validate required environment variables for Solana payments // Validate required environment variables for Solana payments
const requiredEnvVars = [ const requiredEnvVars = [
'REGISTRY_CHAIN_ID', 'NEXT_PUBLIC_REGISTRY_CHAIN_ID',
'REGISTRY_GQL_ENDPOINT', 'NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT',
'REGISTRY_RPC_ENDPOINT', 'NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT',
'REGISTRY_BOND_ID', 'REGISTRY_BOND_ID',
'REGISTRY_AUTHORITY', 'REGISTRY_AUTHORITY',
'REGISTRY_USER_KEY', // This is the same as the prefilled account for LNT transfers 'REGISTRY_USER_KEY', // This is the same as the prefilled account for LNT transfers

View File

@ -1,11 +1,12 @@
'use client'; 'use client';
import { useCallback, useState, useEffect } from 'react'; import { useCallback, useState, useEffect, useMemo } from 'react';
import assert from 'assert'; import assert from 'assert';
import { Connection } from '@solana/web3.js'; import { Connection } from '@solana/web3.js';
import { useConnection, useWallet } from '@solana/wallet-adapter-react'; import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { getRegistry } from '@/config';
import { sendSolanaPayment } from '@/services/solana'; import { sendSolanaPayment } from '@/services/solana';
import { getRequiredTokenInfo, RequiredTokenInfo } from '@/services/jupiter-price'; import { getRequiredTokenInfo, RequiredTokenInfo } from '@/services/jupiter-price';
import { PaymentMethod, PaymentModalProps, PaymentRequest } from '@/types'; import { PaymentMethod, PaymentModalProps, PaymentRequest } from '@/types';
@ -16,6 +17,26 @@ assert(!IS_NAT_GOR_TRANSFER_ENABLED || process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL
const GORBAGANA_RPC_URL = process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL; 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');
const PRICING_RECORD_LRN = process.env.NEXT_PUBLIC_PRICING_RECORD_LRN;
const SUPPORTED_CURRENCY = "USD";
const VALID_PRICING_RECORD_TYPE = "webapp-deployment";
interface DeploymentCostInfo {
amount: string;
currency: string;
}
interface PricingRecordAttributes {
amount: string;
currency: string;
for: string;
type: string;
version: string;
}
export default function PaymentModal({ export default function PaymentModal({
isOpen, isOpen,
onClose, onClose,
@ -23,28 +44,55 @@ export default function PaymentModal({
onPaymentComplete, onPaymentComplete,
}: PaymentModalProps) { }: PaymentModalProps) {
const { selectedPaymentMethod: paymentMethod } = usePaymentMethod(); const { selectedPaymentMethod: paymentMethod } = usePaymentMethod();
const { connection: solanaConnection } = useConnection(); const { connection: solanaConnection } = useConnection();
const { wallet, publicKey } = useWallet();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(''); const [error, setError] = useState('');
const [tokenAmount, setTokenAmount] = useState<number>(0); const [tokenAmount, setTokenAmount] = useState<number>(0);
const [tokenDecimals, setTokenDecimals] = useState<number>(6); // Default fallback const [tokenDecimals, setTokenDecimals] = useState<number>(6); // Default fallback
const [loadingPrice, setLoadingPrice] = useState(false); const [loadingPrice, setLoadingPrice] = useState(true);
const [deploymentCostInfo, setDeploymentCostInfo] = useState<DeploymentCostInfo>();
const { wallet, publicKey } = useWallet(); useEffect(() => {
const registry = getRegistry();
const resolveDeploymentCostInfo = async () => {
const result = await registry.resolveNames([PRICING_RECORD_LRN!])
const PricingRecordAttributes: PricingRecordAttributes = result[0].attributes;
if (PricingRecordAttributes.type !== VALID_PRICING_RECORD_TYPE) {
throw new Error(`Incorrect pricing record type: ${PricingRecordAttributes.type}. Please provide correct pricing record lrn`)
}
if (PricingRecordAttributes.currency !== SUPPORTED_CURRENCY) {
throw new Error(`Unsupported currency found in pricing record: ${PricingRecordAttributes.currency}`)
}
setDeploymentCostInfo({
amount: PricingRecordAttributes.amount,
currency:PricingRecordAttributes.currency
})
}
resolveDeploymentCostInfo();
}, []);
// Get configuration from environment variables // Get configuration from environment variables
const targetUsdAmount = parseFloat(process.env.NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD!); const deploymentCost = useMemo(() => {
if (!deploymentCostInfo) {
return;
}
return parseInt(deploymentCostInfo.amount, 10);
}, [deploymentCostInfo])
const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!; const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!;
const tokenSymbol = process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL; const tokenSymbol = process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL;
// Fetch payment amount based on USD price for both payment methods // Fetch payment amount based on USD price for both payment methods
useEffect(() => { useEffect(() => {
if (!isOpen || !paymentMethod) { if (!isOpen || !deploymentCost || !paymentMethod) return;
setLoadingPrice(false);
return;
}
const fetchPaymentAmount = async () => { const fetchPaymentAmount = async () => {
setLoadingPrice(true); setLoadingPrice(true);
@ -54,10 +102,10 @@ export default function PaymentModal({
let requiredTokenInfo: RequiredTokenInfo let requiredTokenInfo: RequiredTokenInfo
if (paymentMethod === PaymentMethod.NAT_GOR) { if (paymentMethod === PaymentMethod.NAT_GOR) {
// Fetch native GOR amount using solana GOR token price // Fetch native GOR amount using solana GOR token price
requiredTokenInfo = await getRequiredTokenInfo(targetUsdAmount, SOLANA_GOR_MINT_ADDRESS); requiredTokenInfo = await getRequiredTokenInfo(deploymentCost, SOLANA_GOR_MINT_ADDRESS);
} else if (paymentMethod === PaymentMethod.SPL_TOKEN) { } else if (paymentMethod === PaymentMethod.SPL_TOKEN) {
// Fetch SPL token amount using token mint price // Fetch SPL token amount using token mint price
requiredTokenInfo = await getRequiredTokenInfo(targetUsdAmount, mintAddress); requiredTokenInfo = await getRequiredTokenInfo(deploymentCost, mintAddress);
} else { } else {
setError('Invalid payment method'); setError('Invalid payment method');
return; return;
@ -74,7 +122,7 @@ export default function PaymentModal({
}; };
fetchPaymentAmount(); fetchPaymentAmount();
}, [isOpen, paymentMethod, targetUsdAmount, mintAddress]); }, [isOpen, paymentMethod, deploymentCost, mintAddress]);
// Initialize state when modal opens // Initialize state when modal opens
useEffect(() => { useEffect(() => {
@ -186,7 +234,7 @@ export default function PaymentModal({
<div className="relative"> <div className="relative">
<input <input
type="text" type="text"
value={targetUsdAmount} value={`$${deploymentCost}`}
disabled={true} disabled={true}
className="w-full p-3 pr-12 rounded-md" className="w-full p-3 pr-12 rounded-md"
style={{ style={{

View File

@ -22,9 +22,9 @@ export const getRegistry = (): Registry => {
export const getRegistryConfig = (): RegistryConfig => { export const getRegistryConfig = (): RegistryConfig => {
// Validate required environment variables // Validate required environment variables
const requiredEnvVars = [ const requiredEnvVars = [
'REGISTRY_CHAIN_ID', 'NEXT_PUBLIC_REGISTRY_CHAIN_ID',
'REGISTRY_GQL_ENDPOINT', 'NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT',
'REGISTRY_RPC_ENDPOINT', 'NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT',
'REGISTRY_BOND_ID', 'REGISTRY_BOND_ID',
'REGISTRY_AUTHORITY', 'REGISTRY_AUTHORITY',
'REGISTRY_USER_KEY' 'REGISTRY_USER_KEY'
@ -37,9 +37,9 @@ export const getRegistryConfig = (): RegistryConfig => {
} }
return { return {
chainId: process.env.REGISTRY_CHAIN_ID!, chainId: process.env.NEXT_PUBLIC_REGISTRY_CHAIN_ID!,
rpcEndpoint: process.env.REGISTRY_RPC_ENDPOINT!, rpcEndpoint: process.env.NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT!,
gqlEndpoint: process.env.REGISTRY_GQL_ENDPOINT!, gqlEndpoint: process.env.NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT!,
bondId: process.env.REGISTRY_BOND_ID!, bondId: process.env.REGISTRY_BOND_ID!,
authority: process.env.REGISTRY_AUTHORITY!, authority: process.env.REGISTRY_AUTHORITY!,
privateKey: process.env.REGISTRY_USER_KEY!, privateKey: process.env.REGISTRY_USER_KEY!,